playing with qemu usermode emulation on FreeBSD...
Juergen Lock
nox at jelal.kn-bremen.de
Wed Oct 7 22:11:08 UTC 2009
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...)
Enjoy,
Juergen
PS: well I guess I could inline the patch here too so the curious
don't have to extract it out of the update:
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/elfload.c
@@ -126,6 +126,8 @@
regs->rax = 0;
regs->rsp = infop->start_stack;
regs->rip = infop->entry;
+ if (1 /* bsd_type == target_freebsd */)
+ regs->rdi = infop->start_stack;
}
#else
Index: qemu/bsd-user/main.c
@@ -171,26 +171,63 @@
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);
+ abi_ulong arg1, arg2, arg3, arg4, arg5, arg6;
+
+ 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);
+ env->regs[R_EAX] = do_freebsd_syscall(env,
+ env->regs[R_EAX],
+ arg1,
+ arg2,
+ arg3,
+ arg4,
+ arg5,
+ arg6);
+ } 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]);
+ }
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]);
+ 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[10],
+ env->regs[8],
+ env->regs[9]);
+ 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;
break;
#endif
Index: qemu/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,14 +41,88 @@
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;
+}
+
+/* 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;
+
+ 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;
+ ret = get_errno(sysctl(hnamep, namelen, holdp, &holdlen, hnewp, newlen));
+ put_user_ual(holdlen, oldlenp);
+ unlock_user(hnamep, namep, 0);
+ unlock_user(holdp, oldp, holdlen);
+ if (hnewp)
+ unlock_user(hnewp, newp, 0);
+ return ret;
+}
+
/* 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>. */
@@ -103,12 +178,18 @@
case TARGET_FREEBSD_NR_mprotect:
ret = get_errno(target_mprotect(arg1, arg2, arg3));
break;
+ case TARGET_FREEBSD_NR_break:
+ ret = do_obreak(arg1);
+ break;
+ case TARGET_FREEBSD_NR___sysctl:
+ ret = do_freebsd_sysctl(arg1, arg2, arg3, arg4, arg5, arg6);
+ 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);
break;
default:
- ret = syscall(num, arg1, arg2, arg3, arg4, arg5, arg6);
+ ret = get_errno(syscall(num, arg1, arg2, arg3, arg4, arg5, arg6));
break;
}
fail:
Index: qemu/cpu-exec.c
@@ -1252,6 +1252,12 @@
# define TRAP_sig(context) ((context)->sc_trapno)
# define ERROR_sig(context) ((context)->sc_err)
# define MASK_sig(context) ((context)->sc_mask)
+#elif defined(__FreeBSD__)
+#include <sys/ucontext.h>
+#define EIP_sig(context) ((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)
#else
# define EIP_sig(context) ((context)->uc_mcontext.gregs[REG_EIP])
# define TRAP_sig(context) ((context)->uc_mcontext.gregs[REG_TRAPNO])
@@ -1265,6 +1271,8 @@
siginfo_t *info = pinfo;
#if defined(__OpenBSD__)
struct sigcontext *uc = puc;
+#elif defined(__FreeBSD__)
+ ucontext_t *uc = puc;
#else
struct ucontext *uc = puc;
#endif
@@ -1297,6 +1305,12 @@
#define TRAP_sig(context) ((context)->sc_trapno)
#define ERROR_sig(context) ((context)->sc_err)
#define MASK_sig(context) ((context)->sc_mask)
+#elif defined(__FreeBSD__)
+#include <sys/ucontext.h>
+#define PC_sig(context) ((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])
@@ -1313,6 +1327,8 @@
ucontext_t *uc = puc;
#elif defined(__OpenBSD__)
struct sigcontext *uc = puc;
+#elif defined(__FreeBSD__)
+ ucontext_t *uc = puc;
#else
struct ucontext *uc = puc;
#endif
More information about the freebsd-emulation
mailing list