From owner-freebsd-emulation@FreeBSD.ORG Sun Oct 11 23:06:54 2009 Return-Path: Delivered-To: freebsd-emulation@FreeBSD.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 5EE4F1065676 for ; Sun, 11 Oct 2009 23:06:54 +0000 (UTC) (envelope-from nox@jelal.kn-bremen.de) Received: from smtp.kn-bremen.de (gelbbaer.kn-bremen.de [78.46.108.116]) by mx1.freebsd.org (Postfix) with ESMTP id A61718FC08 for ; Sun, 11 Oct 2009 23:06:53 +0000 (UTC) Received: by smtp.kn-bremen.de (Postfix, from userid 10) id E7A571E006E1; Mon, 12 Oct 2009 01:06:51 +0200 (CEST) Received: from triton8.kn-bremen.de (noident@localhost [127.0.0.1]) by triton8.kn-bremen.de (8.14.3/8.14.3) with ESMTP id n9BMIf75056212; Mon, 12 Oct 2009 00:18:41 +0200 (CEST) (envelope-from nox@triton8.kn-bremen.de) Received: (from nox@localhost) by triton8.kn-bremen.de (8.14.3/8.14.3/Submit) id n9BMIfZB056211; Mon, 12 Oct 2009 00:18:41 +0200 (CEST) (envelope-from nox) From: Juergen Lock Date: Mon, 12 Oct 2009 00:18:40 +0200 To: freebsd-emulation@FreeBSD.org Message-ID: <20091011221840.GA55502@triton8.kn-bremen.de> References: <20091007220549.GA65997@triton8.kn-bremen.de> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <20091007220549.GA65997@triton8.kn-bremen.de> User-Agent: Mutt/1.5.20 (2009-06-14) Cc: qemu-devel@nongnu.org, Aleksej Saushev , Toni Subject: Re: playing with qemu usermode emulation on FreeBSD... X-BeenThere: freebsd-emulation@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: Development of Emulators of other operating systems List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 11 Oct 2009 23:06:54 -0000 On Thu, Oct 08, 2009 at 12:05:49AM +0200, Juergen Lock wrote: > I recently noticed there are x86 bsd-user targets now (yeah I totally > missed those commits...) and now got it working a tiny little bit: > I can run > qemu-x86_64 -bsd freebsd /rescue/echo foo bar > here on FreeBSD 8/amd64 and it echoes foo bar as expected, but > segfaults afterwards. :) (in pthread_setcancelstate() invoked from > a guest write() syscall, in case anyone is wondering.) Other things > I tried either exit with errors or segfault as well, and i386 hosts > probably still don't work at all yet. (qemu-i386 here on amd64 does > at least something, but probably needs lock_user() treatment for all > kinds of syscalls, I only tried adding that for sysctl so far.) > > Anyway, here is an emulators/qemu-devel git head snapshot port > update with my current patches (files/patch-bsd-user), feel free to > test/debug/improve: > http://people.freebsd.org/~nox/qemu/qemu-devel-20091007.patch > (For the folks reading this on the qemu list: I shall start doing > `proper' patch submissions later, this is more for the FreeBSD folks > and because I was asked to send what I have...) New version at the same place, which now runs FreeBSD/{i386,sparc64} /rescue/echo on FreeBSD/amd64, the FreeBSD/amd64 target now segfaults in pthread_setcancelstate() invoked from the final writev() tho. Oh and I also uploaded the snapshot tarball so others can now actually build the port too... :) And I have switched to the cpu-exec.c patch posted by Aleksej Saushev on the qemu list and added back amd64 code there. Here is the bsd-user patch again: Index: qemu/bsd-user/elfload.c @@ -126,6 +126,8 @@ static inline void init_thread(struct ta regs->rax = 0; regs->rsp = infop->start_stack; regs->rip = infop->entry; + if (1 /* bsd_type == target_freebsd */) + regs->rdi = infop->start_stack; } #else @@ -249,8 +251,13 @@ static inline void init_thread(struct ta #else if (personality(infop->personality) == PER_LINUX32) regs->u_regs[14] = infop->start_stack - 16 * 4; - else + else { regs->u_regs[14] = infop->start_stack - 16 * 8 - STACK_BIAS; + if (1 /* bsd_type == target_freebsd */) { + regs->u_regs[8] = infop->start_stack; + regs->u_regs[11] = infop->start_stack; + } + } #endif } Index: qemu/bsd-user/freebsd/strace.list @@ -39,6 +39,7 @@ { TARGET_FREEBSD_NR_ftruncate, "ftruncate", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_futimes, "futimes", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getdirentries, "getdirentries", NULL, NULL, NULL }, +{ TARGET_FREEBSD_NR_freebsd6_mmap, "freebsd6_mmap", NULL, NULL, NULL }, { TARGET_FREEBSD_NR_getegid, "getegid", "%s()", NULL, NULL }, { TARGET_FREEBSD_NR_geteuid, "geteuid", "%s()", NULL, NULL }, { TARGET_FREEBSD_NR_getfh, "getfh", NULL, NULL, NULL }, Index: qemu/bsd-user/main.c @@ -179,27 +179,86 @@ void cpu_loop(CPUX86State *env, enum BSD switch(trapnr) { case 0x80: /* syscall from int $0x80 */ - env->regs[R_EAX] = do_openbsd_syscall(env, - env->regs[R_EAX], - env->regs[R_EBX], - env->regs[R_ECX], - env->regs[R_EDX], - env->regs[R_ESI], - env->regs[R_EDI], - env->regs[R_EBP]); + if (bsd_type == target_freebsd) { + abi_ulong params = (abi_ulong) env->regs[R_ESP] + + sizeof(int32_t); + int32_t syscall_nr = env->regs[R_EAX]; + int32_t arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8; + + if (syscall_nr == TARGET_FREEBSD_NR_syscall) { + get_user_s32(syscall_nr, params); + params += sizeof(int32_t); + } else if (syscall_nr == TARGET_FREEBSD_NR___syscall) { + get_user_s32(syscall_nr, params); + params += sizeof(int64_t); + } + get_user_s32(arg1, params); + params += sizeof(int32_t); + get_user_s32(arg2, params); + params += sizeof(int32_t); + get_user_s32(arg3, params); + params += sizeof(int32_t); + get_user_s32(arg4, params); + params += sizeof(int32_t); + get_user_s32(arg5, params); + params += sizeof(int32_t); + get_user_s32(arg6, params); + params += sizeof(int32_t); + get_user_s32(arg7, params); + params += sizeof(int32_t); + get_user_s32(arg8, params); + env->regs[R_EAX] = do_freebsd_syscall(env, + syscall_nr, + arg1, + arg2, + arg3, + arg4, + arg5, + arg6, + arg7, + arg8); + } else { //if (bsd_type == target_openbsd) + env->regs[R_EAX] = do_openbsd_syscall(env, + env->regs[R_EAX], + env->regs[R_EBX], + env->regs[R_ECX], + env->regs[R_EDX], + env->regs[R_ESI], + env->regs[R_EDI], + env->regs[R_EBP]); + } + if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) + env->eflags |= CC_C; + else + env->eflags &= ~CC_C; break; #ifndef TARGET_ABI32 case EXCP_SYSCALL: - /* linux syscall from syscall intruction */ - env->regs[R_EAX] = do_openbsd_syscall(env, - env->regs[R_EAX], - env->regs[R_EDI], - env->regs[R_ESI], - env->regs[R_EDX], - env->regs[10], - env->regs[8], - env->regs[9]); + /* syscall from syscall intruction */ + if (bsd_type == target_freebsd) + env->regs[R_EAX] = do_freebsd_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[R_ECX], + env->regs[8], + env->regs[9], 0, 0); + else { //if (bsd_type == target_openbsd) + env->regs[R_EAX] = do_openbsd_syscall(env, + env->regs[R_EAX], + env->regs[R_EDI], + env->regs[R_ESI], + env->regs[R_EDX], + env->regs[10], + env->regs[8], + env->regs[9]); + } env->eip = env->exception_next_eip; + if (((abi_ulong)env->regs[R_EAX]) >= (abi_ulong)(-515)) + env->eflags |= CC_C; + else + env->eflags &= ~CC_C; break; #endif #if 0 @@ -459,13 +518,17 @@ void cpu_loop(CPUSPARCState *env, enum B case 0x80: #else case 0x100: + /* FreeBSD uses 0x141 for syscalls too */ + case 0x141: + if (bsd_type != target_freebsd) + goto badtrap; #endif syscall_nr = env->gregs[1]; if (bsd_type == target_freebsd) ret = do_freebsd_syscall(env, syscall_nr, env->regwptr[0], env->regwptr[1], env->regwptr[2], env->regwptr[3], - env->regwptr[4], env->regwptr[5]); + env->regwptr[4], env->regwptr[5], 0, 0); else if (bsd_type == target_netbsd) ret = do_netbsd_syscall(env, syscall_nr, env->regwptr[0], env->regwptr[1], @@ -587,6 +650,9 @@ void cpu_loop(CPUSPARCState *env, enum B } break; default: +#ifdef TARGET_SPARC64 + badtrap: +#endif printf ("Unhandled trap: 0x%x\n", trapnr); cpu_dump_state(env, stderr, fprintf, 0); exit (1); Index: qemu/bsd-user/qemu.h @@ -130,7 +130,8 @@ abi_long do_brk(abi_ulong new_brk); void syscall_init(void); 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 do_netbsd_syscall(void *cpu_env, int num, abi_long arg1, abi_long arg2, abi_long arg3, abi_long arg4, abi_long arg5, abi_long arg6); Index: qemu/bsd-user/syscall.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -40,20 +41,222 @@ 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; +} + +#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_. */ 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 +289,18 @@ abi_long do_freebsd_syscall(void *cpu_en 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 +318,20 @@ abi_long do_freebsd_syscall(void *cpu_en 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_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: Index: qemu/bsd-user/syscall_defs.h @@ -106,3 +106,9 @@ #include "freebsd/syscall_nr.h" #include "netbsd/syscall_nr.h" #include "openbsd/syscall_nr.h" + +struct target_iovec { + abi_long iov_base; /* Starting address */ + abi_long iov_len; /* Number of bytes */ +}; + Index: qemu/cpu-exec.c @@ -805,6 +805,20 @@ static inline int handle_cpu_signal(unsi # define TRAP_sig(context) ((context)->uc_mcontext->es.trapno) # define ERROR_sig(context) ((context)->uc_mcontext->es.err) # define MASK_sig(context) ((context)->uc_sigmask) +#elif defined (__NetBSD__) +# include + +# define EIP_sig(context) ((context)->uc_mcontext.__gregs[_REG_EIP]) +# define TRAP_sig(context) ((context)->uc_mcontext.__gregs[_REG_TRAPNO]) +# define ERROR_sig(context) ((context)->uc_mcontext.__gregs[_REG_ERR]) +# define MASK_sig(context) ((context)->uc_sigmask) +#elif defined (__FreeBSD__) || defined(__DragonFly__) +# include + +# define EIP_sig(context) (*((unsigned long*)&(context)->uc_mcontext.mc_eip)) +# define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) +# define ERROR_sig(context) ((context)->uc_mcontext.mc_err) +# define MASK_sig(context) ((context)->uc_sigmask) #elif defined(__OpenBSD__) # define EIP_sig(context) ((context)->sc_eip) # define TRAP_sig(context) ((context)->sc_trapno) @@ -821,7 +835,9 @@ int cpu_signal_handler(int host_signum, void *puc) { siginfo_t *info = pinfo; -#if defined(__OpenBSD__) +#if defined(__NetBSD__) || defined (__FreeBSD__) || defined(__DragonFly__) + ucontext_t *uc = puc; +#elif defined(__OpenBSD__) struct sigcontext *uc = puc; #else struct ucontext *uc = puc; @@ -855,6 +871,13 @@ int cpu_signal_handler(int host_signum, #define TRAP_sig(context) ((context)->sc_trapno) #define ERROR_sig(context) ((context)->sc_err) #define MASK_sig(context) ((context)->sc_mask) +#elif defined (__FreeBSD__) || defined(__DragonFly__) +#include + +#define PC_sig(context) (*((unsigned long*)&(context)->uc_mcontext.mc_rip)) +#define TRAP_sig(context) ((context)->uc_mcontext.mc_trapno) +#define ERROR_sig(context) ((context)->uc_mcontext.mc_err) +#define MASK_sig(context) ((context)->uc_sigmask) #else #define PC_sig(context) ((context)->uc_mcontext.gregs[REG_RIP]) #define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO]) @@ -867,7 +890,7 @@ int cpu_signal_handler(int host_signum, { siginfo_t *info = pinfo; unsigned long pc; -#ifdef __NetBSD__ +#if defined(__NetBSD__) || defined (__FreeBSD__) || defined(__DragonFly__) ucontext_t *uc = puc; #elif defined(__OpenBSD__) struct sigcontext *uc = puc;