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