Statistics
| Branch: | Revision:

root / bsd-user / syscall.c @ a74cdab4

History | View | Annotate | Download (16 kB)

1
/*
2
 *  BSD syscalls
3
 *
4
 *  Copyright (c) 2003 - 2008 Fabrice Bellard
5
 *
6
 *  This program is free software; you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation; either version 2 of the License, or
9
 *  (at your option) any later version.
10
 *
11
 *  This program 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
14
 *  GNU General Public License for more details.
15
 *
16
 *  You should have received a copy of the GNU General Public License
17
 *  along with this program; if not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <stdlib.h>
20
#include <stdio.h>
21
#include <stdint.h>
22
#include <stdarg.h>
23
#include <string.h>
24
#include <errno.h>
25
#include <unistd.h>
26
#include <fcntl.h>
27
#include <time.h>
28
#include <limits.h>
29
#include <sys/types.h>
30
#include <sys/mman.h>
31
#include <sys/syscall.h>
32
#include <sys/param.h>
33
#include <sys/sysctl.h>
34
#include <signal.h>
35
#include <utime.h>
36

    
37
#include "qemu.h"
38
#include "qemu-common.h"
39

    
40
//#define DEBUG
41

    
42
static abi_ulong target_brk;
43
static abi_ulong target_original_brk;
44

    
45
static inline abi_long get_errno(abi_long ret)
46
{
47
    if (ret == -1)
48
        /* XXX need to translate host -> target errnos here */
49
        return -(errno);
50
    else
51
        return ret;
52
}
53

    
54
#define target_to_host_bitmask(x, tbl) (x)
55

    
56
static inline int is_error(abi_long ret)
57
{
58
    return (abi_ulong)ret >= (abi_ulong)(-4096);
59
}
60

    
61
void target_set_brk(abi_ulong new_brk)
62
{
63
    target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk);
64
}
65

    
66
/* do_obreak() must return target errnos. */
67
static abi_long do_obreak(abi_ulong new_brk)
68
{
69
    abi_ulong brk_page;
70
    abi_long mapped_addr;
71
    int new_alloc_size;
72

    
73
    if (!new_brk)
74
        return 0;
75
    if (new_brk < target_original_brk)
76
        return -TARGET_EINVAL;
77

    
78
    brk_page = HOST_PAGE_ALIGN(target_brk);
79

    
80
    /* If the new brk is less than this, set it and we're done... */
81
    if (new_brk < brk_page) {
82
        target_brk = new_brk;
83
        return 0;
84
    }
85

    
86
    /* We need to allocate more memory after the brk... */
87
    new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1);
88
    mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size,
89
                                        PROT_READ|PROT_WRITE,
90
                                        MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0));
91

    
92
    if (!is_error(mapped_addr))
93
        target_brk = new_brk;
94
    else
95
        return mapped_addr;
96

    
97
    return 0;
98
}
99

    
100
#if defined(TARGET_I386)
101
static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms)
102
{
103
    abi_long ret = 0;
104
    abi_ulong val;
105
    int idx;
106

    
107
    switch(op) {
108
#ifdef TARGET_ABI32
109
    case TARGET_FREEBSD_I386_SET_GSBASE:
110
    case TARGET_FREEBSD_I386_SET_FSBASE:
111
        if (op == TARGET_FREEBSD_I386_SET_GSBASE)
112
#else
113
    case TARGET_FREEBSD_AMD64_SET_GSBASE:
114
    case TARGET_FREEBSD_AMD64_SET_FSBASE:
115
        if (op == TARGET_FREEBSD_AMD64_SET_GSBASE)
116
#endif
117
            idx = R_GS;
118
        else
119
            idx = R_FS;
120
        if (get_user(val, parms, abi_ulong))
121
            return -TARGET_EFAULT;
122
        cpu_x86_load_seg(env, idx, 0);
123
        env->segs[idx].base = val;
124
        break;
125
#ifdef TARGET_ABI32
126
    case TARGET_FREEBSD_I386_GET_GSBASE:
127
    case TARGET_FREEBSD_I386_GET_FSBASE:
128
        if (op == TARGET_FREEBSD_I386_GET_GSBASE)
129
#else
130
    case TARGET_FREEBSD_AMD64_GET_GSBASE:
131
    case TARGET_FREEBSD_AMD64_GET_FSBASE:
132
        if (op == TARGET_FREEBSD_AMD64_GET_GSBASE)
133
#endif
134
            idx = R_GS;
135
        else
136
            idx = R_FS;
137
        val = env->segs[idx].base;
138
        if (put_user(val, parms, abi_ulong))
139
            return -TARGET_EFAULT;
140
        break;
141
    /* XXX handle the others... */
142
    default:
143
        ret = -TARGET_EINVAL;
144
        break;
145
    }
146
    return ret;
147
}
148
#endif
149

    
150
#ifdef TARGET_SPARC
151
static abi_long do_freebsd_sysarch(void *env, int op, abi_ulong parms)
152
{
153
    /* XXX handle
154
     * TARGET_FREEBSD_SPARC_UTRAP_INSTALL,
155
     * TARGET_FREEBSD_SPARC_SIGTRAMP_INSTALL
156
     */
157
    return -TARGET_EINVAL;
158
}
159
#endif
160

    
161
#ifdef __FreeBSD__
162
/*
163
 * XXX this uses the undocumented oidfmt interface to find the kind of
164
 * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt()
165
 * (this is mostly copied from src/sbin/sysctl/sysctl.c)
166
 */
167
static int
168
oidfmt(int *oid, int len, char *fmt, uint32_t *kind)
169
{
170
    int qoid[CTL_MAXNAME+2];
171
    uint8_t buf[BUFSIZ];
172
    int i;
173
    size_t j;
174

    
175
    qoid[0] = 0;
176
    qoid[1] = 4;
177
    memcpy(qoid + 2, oid, len * sizeof(int));
178

    
179
    j = sizeof(buf);
180
    i = sysctl(qoid, len + 2, buf, &j, 0, 0);
181
    if (i)
182
        return i;
183

    
184
    if (kind)
185
        *kind = *(uint32_t *)buf;
186

    
187
    if (fmt)
188
        strcpy(fmt, (char *)(buf + sizeof(uint32_t)));
189
    return (0);
190
}
191

    
192
/*
193
 * try and convert sysctl return data for the target.
194
 * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT.
195
 */
196
static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind)
197
{
198
    switch (kind & CTLTYPE) {
199
    case CTLTYPE_INT:
200
    case CTLTYPE_UINT:
201
        *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp);
202
        break;
203
#ifdef TARGET_ABI32
204
    case CTLTYPE_LONG:
205
    case CTLTYPE_ULONG:
206
        *(uint32_t *)holdp = tswap32(*(long *)holdp);
207
        break;
208
#else
209
    case CTLTYPE_LONG:
210
        *(uint64_t *)holdp = tswap64(*(long *)holdp);
211
    case CTLTYPE_ULONG:
212
        *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp);
213
        break;
214
#endif
215
    case CTLTYPE_QUAD:
216
        *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp);
217
        break;
218
    case CTLTYPE_STRING:
219
        break;
220
    default:
221
        /* XXX unhandled */
222
        return -1;
223
    }
224
    return 0;
225
}
226

    
227
/* XXX this needs to be emulated on non-FreeBSD hosts... */
228
static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp,
229
                          abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen)
230
{
231
    abi_long ret;
232
    void *hnamep, *holdp, *hnewp = NULL;
233
    size_t holdlen;
234
    abi_ulong oldlen = 0;
235
    int32_t *snamep = qemu_malloc(sizeof(int32_t) * namelen), *p, *q, i;
236
    uint32_t kind = 0;
237

    
238
    if (oldlenp)
239
        get_user_ual(oldlen, oldlenp);
240
    if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1)))
241
        return -TARGET_EFAULT;
242
    if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1)))
243
        return -TARGET_EFAULT;
244
    if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0)))
245
        return -TARGET_EFAULT;
246
    holdlen = oldlen;
247
    for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++)
248
       *q++ = tswap32(*p);
249
    oidfmt(snamep, namelen, NULL, &kind);
250
    /* XXX swap hnewp */
251
    ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen));
252
    if (!ret)
253
        sysctl_oldcvt(holdp, holdlen, kind);
254
    put_user_ual(holdlen, oldlenp);
255
    unlock_user(hnamep, namep, 0);
256
    unlock_user(holdp, oldp, holdlen);
257
    if (hnewp)
258
        unlock_user(hnewp, newp, 0);
259
    qemu_free(snamep);
260
    return ret;
261
}
262
#endif
263

    
264
/* FIXME
265
 * lock_iovec()/unlock_iovec() have a return code of 0 for success where
266
 * other lock functions have a return code of 0 for failure.
267
 */
268
static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
269
                           int count, int copy)
270
{
271
    struct target_iovec *target_vec;
272
    abi_ulong base;
273
    int i;
274

    
275
    target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
276
    if (!target_vec)
277
        return -TARGET_EFAULT;
278
    for(i = 0;i < count; i++) {
279
        base = tswapl(target_vec[i].iov_base);
280
        vec[i].iov_len = tswapl(target_vec[i].iov_len);
281
        if (vec[i].iov_len != 0) {
282
            vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
283
            /* Don't check lock_user return value. We must call writev even
284
               if a element has invalid base address. */
285
        } else {
286
            /* zero length pointer is ignored */
287
            vec[i].iov_base = NULL;
288
        }
289
    }
290
    unlock_user (target_vec, target_addr, 0);
291
    return 0;
292
}
293

    
294
static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
295
                             int count, int copy)
296
{
297
    struct target_iovec *target_vec;
298
    abi_ulong base;
299
    int i;
300

    
301
    target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
302
    if (!target_vec)
303
        return -TARGET_EFAULT;
304
    for(i = 0;i < count; i++) {
305
        if (target_vec[i].iov_base) {
306
            base = tswapl(target_vec[i].iov_base);
307
            unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
308
        }
309
    }
310
    unlock_user (target_vec, target_addr, 0);
311

    
312
    return 0;
313
}
314

    
315
/* do_syscall() should always have a single exit point at the end so
316
   that actions, such as logging of syscall results, can be performed.
317
   All errnos that do_syscall() returns must be -TARGET_<errcode>. */
318
abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
319
                            abi_long arg2, abi_long arg3, abi_long arg4,
320
                            abi_long arg5, abi_long arg6, abi_long arg7,
321
                            abi_long arg8)
322
{
323
    abi_long ret;
324
    void *p;
325

    
326
#ifdef DEBUG
327
    gemu_log("freebsd syscall %d\n", num);
328
#endif
329
    if(do_strace)
330
        print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
331

    
332
    switch(num) {
333
    case TARGET_FREEBSD_NR_exit:
334
#ifdef TARGET_GPROF
335
        _mcleanup();
336
#endif
337
        gdb_exit(cpu_env, arg1);
338
        /* XXX: should free thread stack and CPU env */
339
        _exit(arg1);
340
        ret = 0; /* avoid warning */
341
        break;
342
    case TARGET_FREEBSD_NR_read:
343
        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
344
            goto efault;
345
        ret = get_errno(read(arg1, p, arg3));
346
        unlock_user(p, arg2, ret);
347
        break;
348
    case TARGET_FREEBSD_NR_write:
349
        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
350
            goto efault;
351
        ret = get_errno(write(arg1, p, arg3));
352
        unlock_user(p, arg2, 0);
353
        break;
354
    case TARGET_FREEBSD_NR_writev:
355
        {
356
            int count = arg3;
357
            struct iovec *vec;
358

    
359
            vec = alloca(count * sizeof(struct iovec));
360
            if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
361
                goto efault;
362
            ret = get_errno(writev(arg1, vec, count));
363
            unlock_iovec(vec, arg2, count, 0);
364
        }
365
        break;
366
    case TARGET_FREEBSD_NR_open:
367
        if (!(p = lock_user_string(arg1)))
368
            goto efault;
369
        ret = get_errno(open(path(p),
370
                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
371
                             arg3));
372
        unlock_user(p, arg1, 0);
373
        break;
374
    case TARGET_FREEBSD_NR_mmap:
375
        ret = get_errno(target_mmap(arg1, arg2, arg3,
376
                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
377
                                    arg5,
378
                                    arg6));
379
        break;
380
    case TARGET_FREEBSD_NR_mprotect:
381
        ret = get_errno(target_mprotect(arg1, arg2, arg3));
382
        break;
383
    case TARGET_FREEBSD_NR_break:
384
        ret = do_obreak(arg1);
385
        break;
386
#ifdef __FreeBSD__
387
    case TARGET_FREEBSD_NR___sysctl:
388
        ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6);
389
        break;
390
#endif
391
    case TARGET_FREEBSD_NR_sysarch:
392
        ret = do_freebsd_sysarch(cpu_env, arg1, arg2);
393
        break;
394
    case TARGET_FREEBSD_NR_syscall:
395
    case TARGET_FREEBSD_NR___syscall:
396
        ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0);
397
        break;
398
    default:
399
        ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
400
        break;
401
    }
402
 fail:
403
#ifdef DEBUG
404
    gemu_log(" = %ld\n", ret);
405
#endif
406
    if (do_strace)
407
        print_freebsd_syscall_ret(num, ret);
408
    return ret;
409
 efault:
410
    ret = -TARGET_EFAULT;
411
    goto fail;
412
}
413

    
414
abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
415
                           abi_long arg2, abi_long arg3, abi_long arg4,
416
                           abi_long arg5, abi_long arg6)
417
{
418
    abi_long ret;
419
    void *p;
420

    
421
#ifdef DEBUG
422
    gemu_log("netbsd syscall %d\n", num);
423
#endif
424
    if(do_strace)
425
        print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
426

    
427
    switch(num) {
428
    case TARGET_NETBSD_NR_exit:
429
#ifdef TARGET_GPROF
430
        _mcleanup();
431
#endif
432
        gdb_exit(cpu_env, arg1);
433
        /* XXX: should free thread stack and CPU env */
434
        _exit(arg1);
435
        ret = 0; /* avoid warning */
436
        break;
437
    case TARGET_NETBSD_NR_read:
438
        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
439
            goto efault;
440
        ret = get_errno(read(arg1, p, arg3));
441
        unlock_user(p, arg2, ret);
442
        break;
443
    case TARGET_NETBSD_NR_write:
444
        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
445
            goto efault;
446
        ret = get_errno(write(arg1, p, arg3));
447
        unlock_user(p, arg2, 0);
448
        break;
449
    case TARGET_NETBSD_NR_open:
450
        if (!(p = lock_user_string(arg1)))
451
            goto efault;
452
        ret = get_errno(open(path(p),
453
                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
454
                             arg3));
455
        unlock_user(p, arg1, 0);
456
        break;
457
    case TARGET_NETBSD_NR_mmap:
458
        ret = get_errno(target_mmap(arg1, arg2, arg3,
459
                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
460
                                    arg5,
461
                                    arg6));
462
        break;
463
    case TARGET_NETBSD_NR_mprotect:
464
        ret = get_errno(target_mprotect(arg1, arg2, arg3));
465
        break;
466
    case TARGET_NETBSD_NR_syscall:
467
    case TARGET_NETBSD_NR___syscall:
468
        ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
469
        break;
470
    default:
471
        ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
472
        break;
473
    }
474
 fail:
475
#ifdef DEBUG
476
    gemu_log(" = %ld\n", ret);
477
#endif
478
    if (do_strace)
479
        print_netbsd_syscall_ret(num, ret);
480
    return ret;
481
 efault:
482
    ret = -TARGET_EFAULT;
483
    goto fail;
484
}
485

    
486
abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
487
                            abi_long arg2, abi_long arg3, abi_long arg4,
488
                            abi_long arg5, abi_long arg6)
489
{
490
    abi_long ret;
491
    void *p;
492

    
493
#ifdef DEBUG
494
    gemu_log("openbsd syscall %d\n", num);
495
#endif
496
    if(do_strace)
497
        print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
498

    
499
    switch(num) {
500
    case TARGET_OPENBSD_NR_exit:
501
#ifdef TARGET_GPROF
502
        _mcleanup();
503
#endif
504
        gdb_exit(cpu_env, arg1);
505
        /* XXX: should free thread stack and CPU env */
506
        _exit(arg1);
507
        ret = 0; /* avoid warning */
508
        break;
509
    case TARGET_OPENBSD_NR_read:
510
        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
511
            goto efault;
512
        ret = get_errno(read(arg1, p, arg3));
513
        unlock_user(p, arg2, ret);
514
        break;
515
    case TARGET_OPENBSD_NR_write:
516
        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
517
            goto efault;
518
        ret = get_errno(write(arg1, p, arg3));
519
        unlock_user(p, arg2, 0);
520
        break;
521
    case TARGET_OPENBSD_NR_open:
522
        if (!(p = lock_user_string(arg1)))
523
            goto efault;
524
        ret = get_errno(open(path(p),
525
                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
526
                             arg3));
527
        unlock_user(p, arg1, 0);
528
        break;
529
    case TARGET_OPENBSD_NR_mmap:
530
        ret = get_errno(target_mmap(arg1, arg2, arg3,
531
                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
532
                                    arg5,
533
                                    arg6));
534
        break;
535
    case TARGET_OPENBSD_NR_mprotect:
536
        ret = get_errno(target_mprotect(arg1, arg2, arg3));
537
        break;
538
    case TARGET_OPENBSD_NR_syscall:
539
    case TARGET_OPENBSD_NR___syscall:
540
        ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
541
        break;
542
    default:
543
        ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
544
        break;
545
    }
546
 fail:
547
#ifdef DEBUG
548
    gemu_log(" = %ld\n", ret);
549
#endif
550
    if (do_strace)
551
        print_openbsd_syscall_ret(num, ret);
552
    return ret;
553
 efault:
554
    ret = -TARGET_EFAULT;
555
    goto fail;
556
}
557

    
558
void syscall_init(void)
559
{
560
}