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    case CTLTYPE_ULONG:
 203        *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp);
 204        break;
 205#endif
 206#ifdef CTLTYPE_U64
 207    case CTLTYPE_S64:
 208    case CTLTYPE_U64:
 209#else
 210    case CTLTYPE_QUAD:
 211#endif
 212        *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp);
 213        break;
 214    case CTLTYPE_STRING:
 215        break;
 216    default:
 217        /* XXX unhandled */
 218        return -1;
 219    }
 220    return 0;
 221}
 222
 223/* XXX this needs to be emulated on non-FreeBSD hosts... */
 224static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp,
 225                          abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen)
 226{
 227    abi_long ret;
 228    void *hnamep, *holdp, *hnewp = NULL;
 229    size_t holdlen;
 230    abi_ulong oldlen = 0;
 231    int32_t *snamep = g_malloc(sizeof(int32_t) * namelen), *p, *q, i;
 232    uint32_t kind = 0;
 233
 234    if (oldlenp)
 235        get_user_ual(oldlen, oldlenp);
 236    if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1)))
 237        return -TARGET_EFAULT;
 238    if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1)))
 239        return -TARGET_EFAULT;
 240    if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0)))
 241        return -TARGET_EFAULT;
 242    holdlen = oldlen;
 243    for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++)
 244       *q++ = tswap32(*p);
 245    oidfmt(snamep, namelen, NULL, &kind);
 246    /* XXX swap hnewp */
 247    ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen));
 248    if (!ret)
 249        sysctl_oldcvt(holdp, holdlen, kind);
 250    put_user_ual(holdlen, oldlenp);
 251    unlock_user(hnamep, namep, 0);
 252    unlock_user(holdp, oldp, holdlen);
 253    if (hnewp)
 254        unlock_user(hnewp, newp, 0);
 255    g_free(snamep);
 256    return ret;
 257}
 258#endif
 259
 260/* FIXME
 261 * lock_iovec()/unlock_iovec() have a return code of 0 for success where
 262 * other lock functions have a return code of 0 for failure.
 263 */
 264static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr,
 265                           int count, int copy)
 266{
 267    struct target_iovec *target_vec;
 268    abi_ulong base;
 269    int i;
 270
 271    target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
 272    if (!target_vec)
 273        return -TARGET_EFAULT;
 274    for(i = 0;i < count; i++) {
 275        base = tswapl(target_vec[i].iov_base);
 276        vec[i].iov_len = tswapl(target_vec[i].iov_len);
 277        if (vec[i].iov_len != 0) {
 278            vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy);
 279            /* Don't check lock_user return value. We must call writev even
 280               if a element has invalid base address. */
 281        } else {
 282            /* zero length pointer is ignored */
 283            vec[i].iov_base = NULL;
 284        }
 285    }
 286    unlock_user (target_vec, target_addr, 0);
 287    return 0;
 288}
 289
 290static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr,
 291                             int count, int copy)
 292{
 293    struct target_iovec *target_vec;
 294    abi_ulong base;
 295    int i;
 296
 297    target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1);
 298    if (!target_vec)
 299        return -TARGET_EFAULT;
 300    for(i = 0;i < count; i++) {
 301        if (target_vec[i].iov_base) {
 302            base = tswapl(target_vec[i].iov_base);
 303            unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0);
 304        }
 305    }
 306    unlock_user (target_vec, target_addr, 0);
 307
 308    return 0;
 309}
 310
 311/* do_syscall() should always have a single exit point at the end so
 312   that actions, such as logging of syscall results, can be performed.
 313   All errnos that do_syscall() returns must be -TARGET_<errcode>. */
 314abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1,
 315                            abi_long arg2, abi_long arg3, abi_long arg4,
 316                            abi_long arg5, abi_long arg6, abi_long arg7,
 317                            abi_long arg8)
 318{
 319    CPUState *cpu = env_cpu(cpu_env);
 320    abi_long ret;
 321    void *p;
 322
 323#ifdef DEBUG
 324    gemu_log("freebsd syscall %d\n", num);
 325#endif
 326    record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
 327
 328    if(do_strace)
 329        print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 330
 331    switch(num) {
 332    case TARGET_FREEBSD_NR_exit:
 333#ifdef TARGET_GPROF
 334        _mcleanup();
 335#endif
 336        gdb_exit(cpu_env, arg1);
 337        qemu_plugin_atexit_cb();
 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
 409    record_syscall_return(cpu, num, ret);
 410    return ret;
 411 efault:
 412    ret = -TARGET_EFAULT;
 413    goto fail;
 414}
 415
 416abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
 417                           abi_long arg2, abi_long arg3, abi_long arg4,
 418                           abi_long arg5, abi_long arg6)
 419{
 420    CPUState *cpu = env_cpu(cpu_env);
 421    abi_long ret;
 422    void *p;
 423
 424#ifdef DEBUG
 425    gemu_log("netbsd syscall %d\n", num);
 426#endif
 427
 428    record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
 429
 430    if(do_strace)
 431        print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 432
 433    switch(num) {
 434    case TARGET_NETBSD_NR_exit:
 435#ifdef TARGET_GPROF
 436        _mcleanup();
 437#endif
 438        gdb_exit(cpu_env, arg1);
 439        qemu_plugin_atexit_cb();
 440        /* XXX: should free thread stack and CPU env */
 441        _exit(arg1);
 442        ret = 0; /* avoid warning */
 443        break;
 444    case TARGET_NETBSD_NR_read:
 445        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
 446            goto efault;
 447        ret = get_errno(read(arg1, p, arg3));
 448        unlock_user(p, arg2, ret);
 449        break;
 450    case TARGET_NETBSD_NR_write:
 451        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
 452            goto efault;
 453        ret = get_errno(write(arg1, p, arg3));
 454        unlock_user(p, arg2, 0);
 455        break;
 456    case TARGET_NETBSD_NR_open:
 457        if (!(p = lock_user_string(arg1)))
 458            goto efault;
 459        ret = get_errno(open(path(p),
 460                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
 461                             arg3));
 462        unlock_user(p, arg1, 0);
 463        break;
 464    case TARGET_NETBSD_NR_mmap:
 465        ret = get_errno(target_mmap(arg1, arg2, arg3,
 466                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
 467                                    arg5,
 468                                    arg6));
 469        break;
 470    case TARGET_NETBSD_NR_mprotect:
 471        ret = get_errno(target_mprotect(arg1, arg2, arg3));
 472        break;
 473    case TARGET_NETBSD_NR_syscall:
 474    case TARGET_NETBSD_NR___syscall:
 475        ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
 476        break;
 477    default:
 478        ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 479        break;
 480    }
 481 fail:
 482#ifdef DEBUG
 483    gemu_log(" = %ld\n", ret);
 484#endif
 485    if (do_strace)
 486        print_netbsd_syscall_ret(num, ret);
 487
 488    record_syscall_return(cpu, num, ret);
 489    return ret;
 490 efault:
 491    ret = -TARGET_EFAULT;
 492    goto fail;
 493}
 494
 495abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
 496                            abi_long arg2, abi_long arg3, abi_long arg4,
 497                            abi_long arg5, abi_long arg6)
 498{
 499    CPUState *cpu = env_cpu(cpu_env);
 500    abi_long ret;
 501    void *p;
 502
 503#ifdef DEBUG
 504    gemu_log("openbsd syscall %d\n", num);
 505#endif
 506
 507    record_syscall_start(cpu, num, arg1, arg2, arg3, arg4, arg5, arg6, 0, 0);
 508
 509    if(do_strace)
 510        print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 511
 512    switch(num) {
 513    case TARGET_OPENBSD_NR_exit:
 514#ifdef TARGET_GPROF
 515        _mcleanup();
 516#endif
 517        gdb_exit(cpu_env, arg1);
 518        qemu_plugin_atexit_cb();
 519        /* XXX: should free thread stack and CPU env */
 520        _exit(arg1);
 521        ret = 0; /* avoid warning */
 522        break;
 523    case TARGET_OPENBSD_NR_read:
 524        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
 525            goto efault;
 526        ret = get_errno(read(arg1, p, arg3));
 527        unlock_user(p, arg2, ret);
 528        break;
 529    case TARGET_OPENBSD_NR_write:
 530        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
 531            goto efault;
 532        ret = get_errno(write(arg1, p, arg3));
 533        unlock_user(p, arg2, 0);
 534        break;
 535    case TARGET_OPENBSD_NR_open:
 536        if (!(p = lock_user_string(arg1)))
 537            goto efault;
 538        ret = get_errno(open(path(p),
 539                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
 540                             arg3));
 541        unlock_user(p, arg1, 0);
 542        break;
 543    case TARGET_OPENBSD_NR_mmap:
 544        ret = get_errno(target_mmap(arg1, arg2, arg3,
 545                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
 546                                    arg5,
 547                                    arg6));
 548        break;
 549    case TARGET_OPENBSD_NR_mprotect:
 550        ret = get_errno(target_mprotect(arg1, arg2, arg3));
 551        break;
 552    case TARGET_OPENBSD_NR_syscall:
 553    case TARGET_OPENBSD_NR___syscall:
 554        ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
 555        break;
 556    default:
 557        ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 558        break;
 559    }
 560 fail:
 561#ifdef DEBUG
 562    gemu_log(" = %ld\n", ret);
 563#endif
 564    if (do_strace)
 565        print_openbsd_syscall_ret(num, ret);
 566
 567    record_syscall_return(cpu, num, ret);
 568    return ret;
 569 efault:
 570    ret = -TARGET_EFAULT;
 571    goto fail;
 572}
 573
 574void syscall_init(void)
 575{
 576}
 577