qemu/bsd-user/syscall.c
<<
>>
Prefs
   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 "qemu/osdep.h"
  20#include "qemu/cutils.h"
  21#include "qemu/path.h"
  22#include <sys/syscall.h>
  23#include <sys/param.h>
  24#include <sys/sysctl.h>
  25#include <utime.h>
  26
  27#include "qemu.h"
  28#include "qemu-common.h"
  29#include "user/syscall-trace.h"
  30
  31//#define DEBUG
  32
  33static abi_ulong target_brk;
  34static abi_ulong target_original_brk;
  35
  36static inline abi_long get_errno(abi_long ret)
  37{
  38    if (ret == -1)
  39        /* XXX need to translate host -> target errnos here */
  40        return -(errno);
  41    else
  42        return ret;
  43}
  44
  45#define target_to_host_bitmask(x, tbl) (x)
  46
  47static inline int is_error(abi_long ret)
  48{
  49    return (abi_ulong)ret >= (abi_ulong)(-4096);
  50}
  51
  52void target_set_brk(abi_ulong new_brk)
  53{
  54    target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk);
  55}
  56
  57/* do_obreak() must return target errnos. */
  58static abi_long do_obreak(abi_ulong new_brk)
  59{
  60    abi_ulong brk_page;
  61    abi_long mapped_addr;
  62    int new_alloc_size;
  63
  64    if (!new_brk)
  65        return 0;
  66    if (new_brk < target_original_brk)
  67        return -TARGET_EINVAL;
  68
  69    brk_page = HOST_PAGE_ALIGN(target_brk);
  70
  71    /* If the new brk is less than this, set it and we're done... */
  72    if (new_brk < brk_page) {
  73        target_brk = new_brk;
  74        return 0;
  75    }
  76
  77    /* We need to allocate more memory after the brk... */
  78    new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1);
  79    mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size,
  80                                        PROT_READ|PROT_WRITE,
  81                                        MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0));
  82
  83    if (!is_error(mapped_addr))
  84        target_brk = new_brk;
  85    else
  86        return mapped_addr;
  87
  88    return 0;
  89}
  90
  91#if defined(TARGET_I386)
  92static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms)
  93{
  94    abi_long ret = 0;
  95    abi_ulong val;
  96    int idx;
  97
  98    switch (op) {
  99#ifdef TARGET_ABI32
 100    case TARGET_FREEBSD_I386_SET_GSBASE:
 101    case TARGET_FREEBSD_I386_SET_FSBASE:
 102        if (op == TARGET_FREEBSD_I386_SET_GSBASE)
 103#else
 104    case TARGET_FREEBSD_AMD64_SET_GSBASE:
 105    case TARGET_FREEBSD_AMD64_SET_FSBASE:
 106        if (op == TARGET_FREEBSD_AMD64_SET_GSBASE)
 107#endif
 108            idx = R_GS;
 109        else
 110            idx = R_FS;
 111        if (get_user(val, parms, abi_ulong))
 112            return -TARGET_EFAULT;
 113        cpu_x86_load_seg(env, idx, 0);
 114        env->segs[idx].base = val;
 115        break;
 116#ifdef TARGET_ABI32
 117    case TARGET_FREEBSD_I386_GET_GSBASE:
 118    case TARGET_FREEBSD_I386_GET_FSBASE:
 119        if (op == TARGET_FREEBSD_I386_GET_GSBASE)
 120#else
 121    case TARGET_FREEBSD_AMD64_GET_GSBASE:
 122    case TARGET_FREEBSD_AMD64_GET_FSBASE:
 123        if (op == TARGET_FREEBSD_AMD64_GET_GSBASE)
 124#endif
 125            idx = R_GS;
 126        else
 127            idx = R_FS;
 128        val = env->segs[idx].base;
 129        if (put_user(val, parms, abi_ulong))
 130            return -TARGET_EFAULT;
 131        break;
 132    /* XXX handle the others... */
 133    default:
 134        ret = -TARGET_EINVAL;
 135        break;
 136    }
 137    return ret;
 138}
 139#endif
 140
 141#ifdef TARGET_SPARC
 142static abi_long do_freebsd_sysarch(void *env, int op, abi_ulong parms)
 143{
 144    /* XXX handle
 145     * TARGET_FREEBSD_SPARC_UTRAP_INSTALL,
 146     * TARGET_FREEBSD_SPARC_SIGTRAMP_INSTALL
 147     */
 148    return -TARGET_EINVAL;
 149}
 150#endif
 151
 152#ifdef __FreeBSD__
 153/*
 154 * XXX this uses the undocumented oidfmt interface to find the kind of
 155 * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt()
 156 * (this is mostly copied from src/sbin/sysctl/sysctl.c)
 157 */
 158static int
 159oidfmt(int *oid, int len, char *fmt, uint32_t *kind)
 160{
 161    int qoid[CTL_MAXNAME+2];
 162    uint8_t buf[BUFSIZ];
 163    int i;
 164    size_t j;
 165
 166    qoid[0] = 0;
 167    qoid[1] = 4;
 168    memcpy(qoid + 2, oid, len * sizeof(int));
 169
 170    j = sizeof(buf);
 171    i = sysctl(qoid, len + 2, buf, &j, 0, 0);
 172    if (i)
 173        return i;
 174
 175    if (kind)
 176        *kind = *(uint32_t *)buf;
 177
 178    if (fmt)
 179        strcpy(fmt, (char *)(buf + sizeof(uint32_t)));
 180    return (0);
 181}
 182
 183/*
 184 * try and convert sysctl return data for the target.
 185 * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT.
 186 */
 187static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind)
 188{
 189    switch (kind & CTLTYPE) {
 190    case CTLTYPE_INT:
 191    case CTLTYPE_UINT:
 192        *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp);
 193        break;
 194#ifdef TARGET_ABI32
 195    case CTLTYPE_LONG:
 196    case CTLTYPE_ULONG:
 197        *(uint32_t *)holdp = tswap32(*(long *)holdp);
 198        break;
 199#else
 200    case CTLTYPE_LONG:
 201        *(uint64_t *)holdp = tswap64(*(long *)holdp);
 202        break;
 203    case CTLTYPE_ULONG:
 204        *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp);
 205        break;
 206#endif
 207#ifdef CTLTYPE_U64
 208    case CTLTYPE_S64:
 209    case CTLTYPE_U64:
 210#else
 211    case CTLTYPE_QUAD:
 212#endif
 213        *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp);
 214        break;
 215    case CTLTYPE_STRING:
 216        break;
 217    default:
 218        /* XXX unhandled */
 219        return -1;
 220    }
 221    return 0;
 222}
 223
 224/* XXX this needs to be emulated on non-FreeBSD hosts... */
 225static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp,
 226                          abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen)
 227{
 228    abi_long ret;
 229    void *hnamep, *holdp, *hnewp = NULL;
 230    size_t holdlen;
 231    abi_ulong oldlen = 0;
 232    int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i;
 233    uint32_t kind = 0;
 234
 235    if (oldlenp)
 236        get_user_ual(oldlen, oldlenp);
 237    if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1)))
 238        return -TARGET_EFAULT;
 239    if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1)))
 240        return -TARGET_EFAULT;
 241    if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0)))
 242        return -TARGET_EFAULT;
 243    holdlen = oldlen;
 244    for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++)
 245       *q++ = tswap32(*p);
 246    oidfmt(snamep, namelen, NULL, &kind);
 247    /* XXX swap hnewp */
 248    ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen));
 249    if (!ret)
 250        sysctl_oldcvt(holdp, holdlen, kind);
 251    put_user_ual(holdlen, oldlenp);
 252    unlock_user(hnamep, namep, 0);
 253    unlock_user(holdp, oldp, holdlen);
 254    if (hnewp)
 255        unlock_user(hnewp, newp, 0);
 256    g_free(snamep);
 257    return ret;
 258}
 259#endif
 260
 261/* FIXME
 262 * lock_iovec()/unlock_iovec() have a return code of 0 for success where
 263 * other lock functions have a return code of 0 for failure.
 264 */
 265static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
 266                           int count, int copy)
 267{
 268    struct target_iovec *target_vec;
 269    abi_ulong base;
 270    int i;
 271
 272    target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
 273    if (!target_vec)
 274        return -TARGET_EFAULT;
 275    for (i = 0;i < count; i++) {
 276        base = tswapl(target_vec[i].iov_base);
 277        vec[i].iov_len = tswapl(target_vec[i].iov_len);
 278        if (vec[i].iov_len != 0) {
 279            vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
 280            /* Don't check lock_user return value. We must call writev even
 281               if a element has invalid base address. */
 282        } else {
 283            /* zero length pointer is ignored */
 284            vec[i].iov_base = NULL;
 285        }
 286    }
 287    unlock_user (target_vec, target_addr, 0);
 288    return 0;
 289}
 290
 291static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
 292                             int count, int copy)
 293{
 294    struct target_iovec *target_vec;
 295    abi_ulong base;
 296    int i;
 297
 298    target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
 299    if (!target_vec)
 300        return -TARGET_EFAULT;
 301    for (i = 0;i < count; i++) {
 302        if (target_vec[i].iov_base) {
 303            base = tswapl(target_vec[i].iov_base);
 304            unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
 305        }
 306    }
 307    unlock_user (target_vec, target_addr, 0);
 308
 309    return 0;
 310}
 311
 312/* do_syscall() should always have a single exit point at the end so
 313   that actions, such as logging of syscall results, can be performed.
 314   All errnos that do_syscall() returns must be -TARGET_<errcode>. */
 315abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
 316                            abi_long arg2, abi_long arg3, abi_long arg4,
 317                            abi_long arg5, abi_long arg6, abi_long arg7,
 318                            abi_long arg8)
 319{
 320    CPUState *cpu = env_cpu(cpu_env);
 321    abi_long ret;
 322    void *p;
 323
 324#ifdef DEBUG
 325    gemu_log("freebsd syscall %d\n", num);
 326#endif
 327    record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
 328
 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 CONFIG_GPROF
 335        _mcleanup();
 336#endif
 337        gdb_exit(arg1);
 338        qemu_plugin_user_exit();
 339        /* XXX: should free thread stack and CPU env */
 340        _exit(arg1);
 341        ret = 0; /* avoid warning */
 342        break;
 343    case TARGET_FREEBSD_NR_read:
 344        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
 345            goto efault;
 346        ret = get_errno(read(arg1, p, arg3));
 347        unlock_user(p, arg2, ret);
 348        break;
 349    case TARGET_FREEBSD_NR_write:
 350        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
 351            goto efault;
 352        ret = get_errno(write(arg1, p, arg3));
 353        unlock_user(p, arg2, 0);
 354        break;
 355    case TARGET_FREEBSD_NR_writev:
 356        {
 357            int count = arg3;
 358            struct iovec *vec;
 359
 360            vec = alloca(count * sizeof(struct iovec));
 361            if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
 362                goto efault;
 363            ret = get_errno(writev(arg1, vec, count));
 364            unlock_iovec(vec, arg2, count, 0);
 365        }
 366        break;
 367    case TARGET_FREEBSD_NR_open:
 368        if (!(p = lock_user_string(arg1)))
 369            goto efault;
 370        ret = get_errno(open(path(p),
 371                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
 372                             arg3));
 373        unlock_user(p, arg1, 0);
 374        break;
 375    case TARGET_FREEBSD_NR_mmap:
 376        ret = get_errno(target_mmap(arg1, arg2, arg3,
 377                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
 378                                    arg5,
 379                                    arg6));
 380        break;
 381    case TARGET_FREEBSD_NR_mprotect:
 382        ret = get_errno(target_mprotect(arg1, arg2, arg3));
 383        break;
 384    case TARGET_FREEBSD_NR_break:
 385        ret = do_obreak(arg1);
 386        break;
 387#ifdef __FreeBSD__
 388    case TARGET_FREEBSD_NR___sysctl:
 389        ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6);
 390        break;
 391#endif
 392    case TARGET_FREEBSD_NR_sysarch:
 393        ret = do_freebsd_sysarch(cpu_env, arg1, arg2);
 394        break;
 395    case TARGET_FREEBSD_NR_syscall:
 396    case TARGET_FREEBSD_NR___syscall:
 397        ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0);
 398        break;
 399    default:
 400        ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
 401        break;
 402    }
 403 fail:
 404#ifdef DEBUG
 405    gemu_log(" = %ld\n", ret);
 406#endif
 407    if (do_strace)
 408        print_freebsd_syscall_ret(num, ret);
 409
 410    record_syscall_return(cpu, num, ret);
 411    return ret;
 412 efault:
 413    ret = -TARGET_EFAULT;
 414    goto fail;
 415}
 416
 417abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
 418                           abi_long arg2, abi_long arg3, abi_long arg4,
 419                           abi_long arg5, abi_long arg6)
 420{
 421    CPUState *cpu = env_cpu(cpu_env);
 422    abi_long ret;
 423    void *p;
 424
 425#ifdef DEBUG
 426    gemu_log("netbsd syscall %d\n", num);
 427#endif
 428
 429    record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
 430
 431    if (do_strace)
 432        print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 433
 434    switch (num) {
 435    case TARGET_NETBSD_NR_exit:
 436#ifdef CONFIG_GPROF
 437        _mcleanup();
 438#endif
 439        gdb_exit(arg1);
 440        qemu_plugin_user_exit();
 441        /* XXX: should free thread stack and CPU env */
 442        _exit(arg1);
 443        ret = 0; /* avoid warning */
 444        break;
 445    case TARGET_NETBSD_NR_read:
 446        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
 447            goto efault;
 448        ret = get_errno(read(arg1, p, arg3));
 449        unlock_user(p, arg2, ret);
 450        break;
 451    case TARGET_NETBSD_NR_write:
 452        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
 453            goto efault;
 454        ret = get_errno(write(arg1, p, arg3));
 455        unlock_user(p, arg2, 0);
 456        break;
 457    case TARGET_NETBSD_NR_open:
 458        if (!(p = lock_user_string(arg1)))
 459            goto efault;
 460        ret = get_errno(open(path(p),
 461                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
 462                             arg3));
 463        unlock_user(p, arg1, 0);
 464        break;
 465    case TARGET_NETBSD_NR_mmap:
 466        ret = get_errno(target_mmap(arg1, arg2, arg3,
 467                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
 468                                    arg5,
 469                                    arg6));
 470        break;
 471    case TARGET_NETBSD_NR_mprotect:
 472        ret = get_errno(target_mprotect(arg1, arg2, arg3));
 473        break;
 474    case TARGET_NETBSD_NR_syscall:
 475    case TARGET_NETBSD_NR___syscall:
 476        ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
 477        break;
 478    default:
 479        ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 480        break;
 481    }
 482 fail:
 483#ifdef DEBUG
 484    gemu_log(" = %ld\n", ret);
 485#endif
 486    if (do_strace)
 487        print_netbsd_syscall_ret(num, ret);
 488
 489    record_syscall_return(cpu, num, ret);
 490    return ret;
 491 efault:
 492    ret = -TARGET_EFAULT;
 493    goto fail;
 494}
 495
 496abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
 497                            abi_long arg2, abi_long arg3, abi_long arg4,
 498                            abi_long arg5, abi_long arg6)
 499{
 500    CPUState *cpu = env_cpu(cpu_env);
 501    abi_long ret;
 502    void *p;
 503
 504#ifdef DEBUG
 505    gemu_log("openbsd syscall %d\n", num);
 506#endif
 507
 508    record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
 509
 510    if (do_strace)
 511        print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 512
 513    switch (num) {
 514    case TARGET_OPENBSD_NR_exit:
 515#ifdef CONFIG_GPROF
 516        _mcleanup();
 517#endif
 518        gdb_exit(arg1);
 519        qemu_plugin_user_exit();
 520        /* XXX: should free thread stack and CPU env */
 521        _exit(arg1);
 522        ret = 0; /* avoid warning */
 523        break;
 524    case TARGET_OPENBSD_NR_read:
 525        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
 526            goto efault;
 527        ret = get_errno(read(arg1, p, arg3));
 528        unlock_user(p, arg2, ret);
 529        break;
 530    case TARGET_OPENBSD_NR_write:
 531        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
 532            goto efault;
 533        ret = get_errno(write(arg1, p, arg3));
 534        unlock_user(p, arg2, 0);
 535        break;
 536    case TARGET_OPENBSD_NR_open:
 537        if (!(p = lock_user_string(arg1)))
 538            goto efault;
 539        ret = get_errno(open(path(p),
 540                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
 541                             arg3));
 542        unlock_user(p, arg1, 0);
 543        break;
 544    case TARGET_OPENBSD_NR_mmap:
 545        ret = get_errno(target_mmap(arg1, arg2, arg3,
 546                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
 547                                    arg5,
 548                                    arg6));
 549        break;
 550    case TARGET_OPENBSD_NR_mprotect:
 551        ret = get_errno(target_mprotect(arg1, arg2, arg3));
 552        break;
 553    case TARGET_OPENBSD_NR_syscall:
 554    case TARGET_OPENBSD_NR___syscall:
 555        ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
 556        break;
 557    default:
 558        ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 559        break;
 560    }
 561 fail:
 562#ifdef DEBUG
 563    gemu_log(" = %ld\n", ret);
 564#endif
 565    if (do_strace)
 566        print_openbsd_syscall_ret(num, ret);
 567
 568    record_syscall_return(cpu, num, ret);
 569    return ret;
 570 efault:
 571    ret = -TARGET_EFAULT;
 572    goto fail;
 573}
 574
 575void syscall_init(void)
 576{
 577}
 578