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/mman.h>
  23#include <sys/syscall.h>
  24#include <sys/param.h>
  25#include <sys/sysctl.h>
  26#include <utime.h>
  27
  28#include "qemu.h"
  29#include "qemu-common.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    abi_long ret;
 320    void *p;
 321
 322#ifdef DEBUG
 323    gemu_log("freebsd syscall %d\n", num);
 324#endif
 325    if(do_strace)
 326        print_freebsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 327
 328    switch(num) {
 329    case TARGET_FREEBSD_NR_exit:
 330#ifdef TARGET_GPROF
 331        _mcleanup();
 332#endif
 333        gdb_exit(cpu_env, arg1);
 334        /* XXX: should free thread stack and CPU env */
 335        _exit(arg1);
 336        ret = 0; /* avoid warning */
 337        break;
 338    case TARGET_FREEBSD_NR_read:
 339        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
 340            goto efault;
 341        ret = get_errno(read(arg1, p, arg3));
 342        unlock_user(p, arg2, ret);
 343        break;
 344    case TARGET_FREEBSD_NR_write:
 345        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
 346            goto efault;
 347        ret = get_errno(write(arg1, p, arg3));
 348        unlock_user(p, arg2, 0);
 349        break;
 350    case TARGET_FREEBSD_NR_writev:
 351        {
 352            int count = arg3;
 353            struct iovec *vec;
 354
 355            vec = alloca(count * sizeof(struct iovec));
 356            if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0)
 357                goto efault;
 358            ret = get_errno(writev(arg1, vec, count));
 359            unlock_iovec(vec, arg2, count, 0);
 360        }
 361        break;
 362    case TARGET_FREEBSD_NR_open:
 363        if (!(p = lock_user_string(arg1)))
 364            goto efault;
 365        ret = get_errno(open(path(p),
 366                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
 367                             arg3));
 368        unlock_user(p, arg1, 0);
 369        break;
 370    case TARGET_FREEBSD_NR_mmap:
 371        ret = get_errno(target_mmap(arg1, arg2, arg3,
 372                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
 373                                    arg5,
 374                                    arg6));
 375        break;
 376    case TARGET_FREEBSD_NR_mprotect:
 377        ret = get_errno(target_mprotect(arg1, arg2, arg3));
 378        break;
 379    case TARGET_FREEBSD_NR_break:
 380        ret = do_obreak(arg1);
 381        break;
 382#ifdef __FreeBSD__
 383    case TARGET_FREEBSD_NR___sysctl:
 384        ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6);
 385        break;
 386#endif
 387    case TARGET_FREEBSD_NR_sysarch:
 388        ret = do_freebsd_sysarch(cpu_env, arg1, arg2);
 389        break;
 390    case TARGET_FREEBSD_NR_syscall:
 391    case TARGET_FREEBSD_NR___syscall:
 392        ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0);
 393        break;
 394    default:
 395        ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8));
 396        break;
 397    }
 398 fail:
 399#ifdef DEBUG
 400    gemu_log(" = %ld\n", ret);
 401#endif
 402    if (do_strace)
 403        print_freebsd_syscall_ret(num, ret);
 404    return ret;
 405 efault:
 406    ret = -TARGET_EFAULT;
 407    goto fail;
 408}
 409
 410abi_long do_netbsd_syscall(void *cpu_env, int num, abi_long arg1,
 411                           abi_long arg2, abi_long arg3, abi_long arg4,
 412                           abi_long arg5, abi_long arg6)
 413{
 414    abi_long ret;
 415    void *p;
 416
 417#ifdef DEBUG
 418    gemu_log("netbsd syscall %d\n", num);
 419#endif
 420    if(do_strace)
 421        print_netbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 422
 423    switch(num) {
 424    case TARGET_NETBSD_NR_exit:
 425#ifdef TARGET_GPROF
 426        _mcleanup();
 427#endif
 428        gdb_exit(cpu_env, arg1);
 429        /* XXX: should free thread stack and CPU env */
 430        _exit(arg1);
 431        ret = 0; /* avoid warning */
 432        break;
 433    case TARGET_NETBSD_NR_read:
 434        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
 435            goto efault;
 436        ret = get_errno(read(arg1, p, arg3));
 437        unlock_user(p, arg2, ret);
 438        break;
 439    case TARGET_NETBSD_NR_write:
 440        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
 441            goto efault;
 442        ret = get_errno(write(arg1, p, arg3));
 443        unlock_user(p, arg2, 0);
 444        break;
 445    case TARGET_NETBSD_NR_open:
 446        if (!(p = lock_user_string(arg1)))
 447            goto efault;
 448        ret = get_errno(open(path(p),
 449                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
 450                             arg3));
 451        unlock_user(p, arg1, 0);
 452        break;
 453    case TARGET_NETBSD_NR_mmap:
 454        ret = get_errno(target_mmap(arg1, arg2, arg3,
 455                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
 456                                    arg5,
 457                                    arg6));
 458        break;
 459    case TARGET_NETBSD_NR_mprotect:
 460        ret = get_errno(target_mprotect(arg1, arg2, arg3));
 461        break;
 462    case TARGET_NETBSD_NR_syscall:
 463    case TARGET_NETBSD_NR___syscall:
 464        ret = do_netbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
 465        break;
 466    default:
 467        ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 468        break;
 469    }
 470 fail:
 471#ifdef DEBUG
 472    gemu_log(" = %ld\n", ret);
 473#endif
 474    if (do_strace)
 475        print_netbsd_syscall_ret(num, ret);
 476    return ret;
 477 efault:
 478    ret = -TARGET_EFAULT;
 479    goto fail;
 480}
 481
 482abi_long do_openbsd_syscall(void *cpu_env, int num, abi_long arg1,
 483                            abi_long arg2, abi_long arg3, abi_long arg4,
 484                            abi_long arg5, abi_long arg6)
 485{
 486    abi_long ret;
 487    void *p;
 488
 489#ifdef DEBUG
 490    gemu_log("openbsd syscall %d\n", num);
 491#endif
 492    if(do_strace)
 493        print_openbsd_syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 494
 495    switch(num) {
 496    case TARGET_OPENBSD_NR_exit:
 497#ifdef TARGET_GPROF
 498        _mcleanup();
 499#endif
 500        gdb_exit(cpu_env, arg1);
 501        /* XXX: should free thread stack and CPU env */
 502        _exit(arg1);
 503        ret = 0; /* avoid warning */
 504        break;
 505    case TARGET_OPENBSD_NR_read:
 506        if (!(p = lock_user(VERIFY_WRITE, arg2, arg3, 0)))
 507            goto efault;
 508        ret = get_errno(read(arg1, p, arg3));
 509        unlock_user(p, arg2, ret);
 510        break;
 511    case TARGET_OPENBSD_NR_write:
 512        if (!(p = lock_user(VERIFY_READ, arg2, arg3, 1)))
 513            goto efault;
 514        ret = get_errno(write(arg1, p, arg3));
 515        unlock_user(p, arg2, 0);
 516        break;
 517    case TARGET_OPENBSD_NR_open:
 518        if (!(p = lock_user_string(arg1)))
 519            goto efault;
 520        ret = get_errno(open(path(p),
 521                             target_to_host_bitmask(arg2, fcntl_flags_tbl),
 522                             arg3));
 523        unlock_user(p, arg1, 0);
 524        break;
 525    case TARGET_OPENBSD_NR_mmap:
 526        ret = get_errno(target_mmap(arg1, arg2, arg3,
 527                                    target_to_host_bitmask(arg4, mmap_flags_tbl),
 528                                    arg5,
 529                                    arg6));
 530        break;
 531    case TARGET_OPENBSD_NR_mprotect:
 532        ret = get_errno(target_mprotect(arg1, arg2, arg3));
 533        break;
 534    case TARGET_OPENBSD_NR_syscall:
 535    case TARGET_OPENBSD_NR___syscall:
 536        ret = do_openbsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0);
 537        break;
 538    default:
 539        ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
 540        break;
 541    }
 542 fail:
 543#ifdef DEBUG
 544    gemu_log(" = %ld\n", ret);
 545#endif
 546    if (do_strace)
 547        print_openbsd_syscall_ret(num, ret);
 548    return ret;
 549 efault:
 550    ret = -TARGET_EFAULT;
 551    goto fail;
 552}
 553
 554void syscall_init(void)
 555{
 556}
 557