summary refs log tree commit diff stats
path: root/linux-user
diff options
context:
space:
mode:
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-03-22 17:31:38 +0000
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>2003-03-22 17:31:38 +0000
commit1b6b029e40c4297ce9c27e0f8b8ae177085c990a (patch)
treeffcae72b2e16e395ec983f3718adcf9a981b9a66 /linux-user
parent612384d77146639cebdc9b71c87ee4a94bf44501 (diff)
downloadfocaccia-qemu-1b6b029e40c4297ce9c27e0f8b8ae177085c990a.tar.gz
focaccia-qemu-1b6b029e40c4297ce9c27e0f8b8ae177085c990a.zip
basic clone() support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'linux-user')
-rw-r--r--linux-user/main.c68
-rw-r--r--linux-user/qemu.h4
-rw-r--r--linux-user/syscall.c66
-rw-r--r--linux-user/syscall_defs.h5
4 files changed, 105 insertions, 38 deletions
diff --git a/linux-user/main.c b/linux-user/main.c
index 3222629b27..cd08c474c4 100644
--- a/linux-user/main.c
+++ b/linux-user/main.c
@@ -104,6 +104,40 @@ void write_dt(void *ptr, unsigned long addr, unsigned long limit,
 
 uint64_t gdt_table[6];
 
+void cpu_loop(struct CPUX86State *env)
+{
+    for(;;) {
+        int err;
+        uint8_t *pc;
+        
+        err = cpu_x86_exec(env);
+        pc = env->seg_cache[R_CS].base + env->eip;
+        switch(err) {
+        case EXCP0D_GPF:
+            if (pc[0] == 0xcd && pc[1] == 0x80) {
+                /* syscall */
+                env->eip += 2;
+                env->regs[R_EAX] = do_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]);
+            } else {
+                goto trap_error;
+            }
+            break;
+        default:
+        trap_error:
+            fprintf(stderr, "0x%08lx: Unknown exception %d, aborting\n", 
+                    (long)pc, err);
+            abort();
+        }
+    }
+}
+
 void usage(void)
 {
     printf("gemu version " GEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
@@ -113,8 +147,6 @@ void usage(void)
     exit(1);
 }
 
-
-
 int main(int argc, char **argv)
 {
     const char *filename;
@@ -193,35 +225,7 @@ int main(int argc, char **argv)
     cpu_x86_load_seg(env, R_FS, __USER_DS);
     cpu_x86_load_seg(env, R_GS, __USER_DS);
 
-    for(;;) {
-        int err;
-        uint8_t *pc;
-        
-        err = cpu_x86_exec(env);
-        pc = env->seg_cache[R_CS].base + env->eip;
-        switch(err) {
-        case EXCP0D_GPF:
-            if (pc[0] == 0xcd && pc[1] == 0x80) {
-                /* syscall */
-                env->eip += 2;
-                env->regs[R_EAX] = do_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]);
-            } else {
-                goto trap_error;
-            }
-            break;
-        default:
-        trap_error:
-            fprintf(stderr, "0x%08lx: Unknown exception %d, aborting\n", 
-                    (long)pc, err);
-            abort();
-        }
-    }
+    cpu_loop(env);
+    /* never exits */
     return 0;
 }
diff --git a/linux-user/qemu.h b/linux-user/qemu.h
index 4f09e6fde2..ae86176c35 100644
--- a/linux-user/qemu.h
+++ b/linux-user/qemu.h
@@ -51,7 +51,7 @@ void syscall_init(void);
 long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, 
                 long arg4, long arg5, long arg6);
 void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
-
-
+struct CPUX86State;
+void cpu_loop(struct CPUX86State *env);
 
 #endif
diff --git a/linux-user/syscall.c b/linux-user/syscall.c
index afdf189676..da54a0a186 100644
--- a/linux-user/syscall.c
+++ b/linux-user/syscall.c
@@ -762,8 +762,48 @@ int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecou
     }
     return ret;
 }
+
+/* this stack is the equivalent of the kernel stack associated with a
+   thread/process */
+#define NEW_STACK_SIZE 8192
+
+static int clone_func(void *arg)
+{
+    CPUX86State *env = arg;
+    cpu_loop(env);
+    /* never exits */
+    return 0;
+}
+
+int do_fork(CPUX86State *env, unsigned int flags, unsigned long newsp)
+{
+    int ret;
+    uint8_t *new_stack;
+    CPUX86State *new_env;
+    
+    if (flags & CLONE_VM) {
+        if (!newsp)
+            newsp = env->regs[R_ESP];
+        new_stack = malloc(NEW_STACK_SIZE);
+        
+        /* we create a new CPU instance. */
+        new_env = cpu_x86_init();
+        memcpy(new_env, env, sizeof(CPUX86State));
+        new_env->regs[R_ESP] = newsp;
+        new_env->regs[R_EAX] = 0;
+        ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
+    } else {
+        /* if no CLONE_VM, we consider it is a fork */
+        if ((flags & ~CSIGNAL) != 0)
+            return -EINVAL;
+        ret = fork();
+    }
+    return ret;
+}
+
 #endif
 
+
 void syscall_init(void)
 {
 #define STRUCT(name, list...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def); 
@@ -788,6 +828,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
 #ifdef HAVE_GPROF
         _mcleanup();
 #endif
+        /* XXX: should free thread stack and CPU env */
         _exit(arg1);
         ret = 0; /* avoid warning */
         break;
@@ -807,7 +848,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
         ret = do_brk((char *)arg1);
         break;
     case TARGET_NR_fork:
-        ret = get_errno(fork());
+        ret = get_errno(do_fork(cpu_env, SIGCHLD, 0));
         break;
     case TARGET_NR_waitpid:
         {
@@ -1241,7 +1282,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
     case TARGET_NR_sigreturn:
         goto unimplemented;
     case TARGET_NR_clone:
-        goto unimplemented;
+        ret = get_errno(do_fork(cpu_env, arg1, arg2));
+        break;
     case TARGET_NR_setdomainname:
         ret = get_errno(setdomainname((const char *)arg1, arg2));
         break;
@@ -1310,7 +1352,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
     case TARGET_NR_sysfs:
         goto unimplemented;
     case TARGET_NR_personality:
-        ret = get_errno(mprotect((void *)arg1, arg2, arg3));
+        ret = get_errno(personality(arg1));
         break;
     case TARGET_NR_afs_syscall:
         goto unimplemented;
@@ -1447,7 +1489,23 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
     case TARGET_NR_sched_get_priority_max:
     case TARGET_NR_sched_get_priority_min:
     case TARGET_NR_sched_rr_get_interval:
+        goto unimplemented;
+        
     case TARGET_NR_nanosleep:
+        {
+            struct target_timespec *target_req = (void *)arg1;
+            struct target_timespec *target_rem = (void *)arg2;
+            struct timespec req, rem;
+            req.tv_sec = tswapl(target_req->tv_sec);
+            req.tv_nsec = tswapl(target_req->tv_nsec);
+            ret = get_errno(nanosleep(&req, &rem));
+            if (target_rem) {
+                target_rem->tv_sec = tswapl(rem.tv_sec);
+                target_rem->tv_nsec = tswapl(rem.tv_nsec);
+            }
+        }
+        break;
+
     case TARGET_NR_mremap:
     case TARGET_NR_setresuid:
     case TARGET_NR_getresuid:
@@ -1481,7 +1539,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
     case TARGET_NR_getpmsg:
     case TARGET_NR_putpmsg:
     case TARGET_NR_vfork:
-        ret = get_errno(vfork());
+        ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
         break;
     case TARGET_NR_ugetrlimit:
     case TARGET_NR_truncate64:
diff --git a/linux-user/syscall_defs.h b/linux-user/syscall_defs.h
index 8b2d6bd756..6b0a714cb7 100644
--- a/linux-user/syscall_defs.h
+++ b/linux-user/syscall_defs.h
@@ -24,6 +24,11 @@ struct target_timeval {
     target_long tv_usec;
 };
 
+struct target_timespec {
+    target_long tv_sec;
+    target_long tv_nsec;
+};
+
 struct target_iovec {
     target_long iov_base;   /* Starting address */
     target_long iov_len;   /* Number of bytes */