diff options
Diffstat (limited to 'bsd-user/syscall.c')
-rw-r--r-- | bsd-user/syscall.c | 295 |
1 files changed, 291 insertions, 4 deletions
diff --git a/bsd-user/syscall.c b/bsd-user/syscall.c index 3cff35765a..c77586ee96 100644 --- a/bsd-user/syscall.c +++ b/bsd-user/syscall.c @@ -29,6 +29,7 @@ #include <sys/types.h> #include <sys/mman.h> #include <sys/syscall.h> +#include <sys/sysctl.h> #include <signal.h> #include <utime.h> @@ -40,20 +41,283 @@ static abi_ulong target_brk; static abi_ulong target_original_brk; -#define get_errno(x) (x) +static inline abi_long get_errno(abi_long ret) +{ + if (ret == -1) + /* XXX need to translate host -> target errnos here */ + return -(errno); + else + return ret; +} + #define target_to_host_bitmask(x, tbl) (x) +static inline int is_error(abi_long ret) +{ + return (abi_ulong)ret >= (abi_ulong)(-4096); +} + void target_set_brk(abi_ulong new_brk) { target_original_brk = target_brk = HOST_PAGE_ALIGN(new_brk); } +/* do_obreak() must return target errnos. */ +static abi_long do_obreak(abi_ulong new_brk) +{ + abi_ulong brk_page; + abi_long mapped_addr; + int new_alloc_size; + + if (!new_brk) + return 0; + if (new_brk < target_original_brk) + return -TARGET_EINVAL; + + brk_page = HOST_PAGE_ALIGN(target_brk); + + /* If the new brk is less than this, set it and we're done... */ + if (new_brk < brk_page) { + target_brk = new_brk; + return 0; + } + + /* We need to allocate more memory after the brk... */ + new_alloc_size = HOST_PAGE_ALIGN(new_brk - brk_page + 1); + mapped_addr = get_errno(target_mmap(brk_page, new_alloc_size, + PROT_READ|PROT_WRITE, + MAP_ANON|MAP_FIXED|MAP_PRIVATE, -1, 0)); + + if (!is_error(mapped_addr)) + target_brk = new_brk; + else + return mapped_addr; + + return 0; +} + +#if defined(TARGET_I386) +static abi_long do_freebsd_sysarch(CPUX86State *env, int op, abi_ulong parms) +{ + abi_long ret = 0; + abi_ulong val; + int idx; + + switch(op) { +#ifdef TARGET_ABI32 + case TARGET_FREEBSD_I386_SET_GSBASE: + case TARGET_FREEBSD_I386_SET_FSBASE: + if (op == TARGET_FREEBSD_I386_SET_GSBASE) +#else + case TARGET_FREEBSD_AMD64_SET_GSBASE: + case TARGET_FREEBSD_AMD64_SET_FSBASE: + if (op == TARGET_FREEBSD_AMD64_SET_GSBASE) +#endif + idx = R_GS; + else + idx = R_FS; + if (get_user(val, parms, abi_ulong)) + return -TARGET_EFAULT; + cpu_x86_load_seg(env, idx, 0); + env->segs[idx].base = val; + break; +#ifdef TARGET_ABI32 + case TARGET_FREEBSD_I386_GET_GSBASE: + case TARGET_FREEBSD_I386_GET_FSBASE: + if (op == TARGET_FREEBSD_I386_GET_GSBASE) +#else + case TARGET_FREEBSD_AMD64_GET_GSBASE: + case TARGET_FREEBSD_AMD64_GET_FSBASE: + if (op == TARGET_FREEBSD_AMD64_GET_GSBASE) +#endif + idx = R_GS; + else + idx = R_FS; + val = env->segs[idx].base; + if (put_user(val, parms, abi_ulong)) + return -TARGET_EFAULT; + break; + /* XXX handle the others... */ + default: + ret = -TARGET_EINVAL; + break; + } + return ret; +} +#endif + +#ifdef TARGET_SPARC +static abi_long do_freebsd_sysarch(void *env, int op, abi_ulong parms) +{ + /* XXX handle + * TARGET_FREEBSD_SPARC_UTRAP_INSTALL, + * TARGET_FREEBSD_SPARC_SIGTRAMP_INSTALL + */ + return -TARGET_EINVAL; +} +#endif + +#ifdef __FreeBSD__ +/* + * XXX this uses the undocumented oidfmt interface to find the kind of + * a requested sysctl, see /sys/kern/kern_sysctl.c:sysctl_sysctl_oidfmt() + * (this is mostly copied from src/sbin/sysctl/sysctl.c) + */ +static int +oidfmt(int *oid, int len, char *fmt, uint32_t *kind) +{ + int qoid[CTL_MAXNAME+2]; + uint8_t buf[BUFSIZ]; + int i; + size_t j; + + qoid[0] = 0; + qoid[1] = 4; + memcpy(qoid + 2, oid, len * sizeof(int)); + + j = sizeof(buf); + i = sysctl(qoid, len + 2, buf, &j, 0, 0); + if (i) + return i; + + if (kind) + *kind = *(uint32_t *)buf; + + if (fmt) + strcpy(fmt, (char *)(buf + sizeof(uint32_t))); + return (0); +} + +/* + * try and convert sysctl return data for the target. + * XXX doesn't handle CTLTYPE_OPAQUE and CTLTYPE_STRUCT. + */ +static int sysctl_oldcvt(void *holdp, size_t holdlen, uint32_t kind) +{ + switch (kind & CTLTYPE) { + case CTLTYPE_INT: + case CTLTYPE_UINT: + *(uint32_t *)holdp = tswap32(*(uint32_t *)holdp); + break; +#ifdef TARGET_ABI32 + case CTLTYPE_LONG: + case CTLTYPE_ULONG: + *(uint32_t *)holdp = tswap32(*(long *)holdp); + break; +#else + case CTLTYPE_LONG: + *(uint64_t *)holdp = tswap64(*(long *)holdp); + case CTLTYPE_ULONG: + *(uint64_t *)holdp = tswap64(*(unsigned long *)holdp); + break; +#endif + case CTLTYPE_QUAD: + *(uint64_t *)holdp = tswap64(*(uint64_t *)holdp); + break; + case CTLTYPE_STRING: + break; + default: + /* XXX unhandled */ + return -1; + } + return 0; +} + +/* XXX this needs to be emulated on non-FreeBSD hosts... */ +static abi_long do_freebsd_sysctl(abi_ulong namep, int32_t namelen, abi_ulong oldp, + abi_ulong oldlenp, abi_ulong newp, abi_ulong newlen) +{ + abi_long ret; + void *hnamep, *holdp, *hnewp = NULL; + size_t holdlen; + abi_ulong oldlen = 0; + int32_t *snamep = qemu_malloc(sizeof(int32_t) * namelen), *p, *q, i; + uint32_t kind = 0; + + if (oldlenp) + get_user_ual(oldlen, oldlenp); + if (!(hnamep = lock_user(VERIFY_READ, namep, namelen, 1))) + return -TARGET_EFAULT; + if (newp && !(hnewp = lock_user(VERIFY_READ, newp, newlen, 1))) + return -TARGET_EFAULT; + if (!(holdp = lock_user(VERIFY_WRITE, oldp, oldlen, 0))) + return -TARGET_EFAULT; + holdlen = oldlen; + for (p = hnamep, q = snamep, i = 0; i < namelen; p++, i++) + *q++ = tswap32(*p); + oidfmt(snamep, namelen, NULL, &kind); + /* XXX swap hnewp */ + ret = get_errno(sysctl(snamep, namelen, holdp, &holdlen, hnewp, newlen)); + if (!ret) + sysctl_oldcvt(holdp, holdlen, kind); + put_user_ual(holdlen, oldlenp); + unlock_user(hnamep, namep, 0); + unlock_user(holdp, oldp, holdlen); + if (hnewp) + unlock_user(hnewp, newp, 0); + qemu_free(snamep); + return ret; +} +#endif + +/* FIXME + * lock_iovec()/unlock_iovec() have a return code of 0 for success where + * other lock functions have a return code of 0 for failure. + */ +static abi_long lock_iovec(int type, struct iovec *vec, abi_ulong target_addr, + int count, int copy) +{ + struct target_iovec *target_vec; + abi_ulong base; + int i; + + target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1); + if (!target_vec) + return -TARGET_EFAULT; + for(i = 0;i < count; i++) { + base = tswapl(target_vec[i].iov_base); + vec[i].iov_len = tswapl(target_vec[i].iov_len); + if (vec[i].iov_len != 0) { + vec[i].iov_base = lock_user(type, base, vec[i].iov_len, copy); + /* Don't check lock_user return value. We must call writev even + if a element has invalid base address. */ + } else { + /* zero length pointer is ignored */ + vec[i].iov_base = NULL; + } + } + unlock_user (target_vec, target_addr, 0); + return 0; +} + +static abi_long unlock_iovec(struct iovec *vec, abi_ulong target_addr, + int count, int copy) +{ + struct target_iovec *target_vec; + abi_ulong base; + int i; + + target_vec = lock_user(VERIFY_READ, target_addr, count * sizeof(struct target_iovec), 1); + if (!target_vec) + return -TARGET_EFAULT; + for(i = 0;i < count; i++) { + if (target_vec[i].iov_base) { + base = tswapl(target_vec[i].iov_base); + unlock_user(vec[i].iov_base, base, copy ? vec[i].iov_len : 0); + } + } + unlock_user (target_vec, target_addr, 0); + + return 0; +} + /* do_syscall() should always have a single exit point at the end so that actions, such as logging of syscall results, can be performed. All errnos that do_syscall() returns must be -TARGET_<errcode>. */ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, - abi_long arg5, abi_long arg6) + abi_long arg5, abi_long arg6, abi_long arg7, + abi_long arg8) { abi_long ret; void *p; @@ -86,6 +350,18 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, ret = get_errno(write(arg1, p, arg3)); unlock_user(p, arg2, 0); break; + case TARGET_FREEBSD_NR_writev: + { + int count = arg3; + struct iovec *vec; + + vec = alloca(count * sizeof(struct iovec)); + if (lock_iovec(VERIFY_READ, vec, arg2, count, 1) < 0) + goto efault; + ret = get_errno(writev(arg1, vec, count)); + unlock_iovec(vec, arg2, count, 0); + } + break; case TARGET_FREEBSD_NR_open: if (!(p = lock_user_string(arg1))) goto efault; @@ -103,12 +379,23 @@ abi_long do_freebsd_syscall(void *cpu_env, int num, abi_long arg1, case TARGET_FREEBSD_NR_mprotect: ret = get_errno(target_mprotect(arg1, arg2, arg3)); break; + case TARGET_FREEBSD_NR_break: + ret = do_obreak(arg1); + break; +#ifdef __FreeBSD__ + case TARGET_FREEBSD_NR___sysctl: + ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6); + break; +#endif + case TARGET_FREEBSD_NR_sysarch: + ret = do_freebsd_sysarch(cpu_env, arg1, arg2); + break; case TARGET_FREEBSD_NR_syscall: case TARGET_FREEBSD_NR___syscall: - ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,0); + ret = do_freebsd_syscall(cpu_env,arg1 & 0xffff,arg2,arg3,arg4,arg5,arg6,arg7,arg8,0); break; default: - ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6); + ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)); break; } fail: |