]> git.proxmox.com Git - mirror_qemu.git/commitdiff
basic clone() support
authorbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 22 Mar 2003 17:31:38 +0000 (17:31 +0000)
committerbellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 22 Mar 2003 17:31:38 +0000 (17:31 +0000)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162

14 files changed:
TODO
exec-i386.c
exec-i386.h
linux-user/main.c
linux-user/qemu.h
linux-user/syscall.c
linux-user/syscall_defs.h
op-i386.c
opc-i386.h
tests/Makefile
tests/testclone.c [new file with mode: 0644]
tests/testsig.c [new file with mode: 0644]
tests/testthread.c [new file with mode: 0644]
translate-i386.c

diff --git a/TODO b/TODO
index 36efe4e9e45970e1dfbdcbf1827b94967b62cdb2..5c7c963df31e790df1deceabf547bfdf07511639 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,8 +1,9 @@
-- overrides/16bit for string ops
-- optimize translated cache chaining (DLL PLT-like system)
-- 64 bit syscalls
+- verify thread support (clone() and various locks)
 - signals
-- threads
+- optimize translated cache chaining (DLL PLT-like system)
+- vm86 syscall support
+- overrides/16bit for string ops
+- more syscalls (in particular all 64 bit ones)
 - make it self runnable (use same trick as ld.so : include its own relocator and libc)
 - improved 16 bit support 
 - fix FPU exceptions (in particular: gen_op_fpush not before mem load)
index e83d220369c2a8a20939fe960dedc6d028cc3909..573ba0a8c6f6031f3f8ff9fbeb84f89dbda8b99e 100644 (file)
@@ -52,6 +52,52 @@ int nb_tbs;
 uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
 uint8_t *code_gen_ptr;
 
+/* thread support */
+
+#ifdef __powerpc__
+static inline int testandset (int *p)
+{
+    int ret;
+    __asm__ __volatile__ (
+                          "0:    lwarx %0,0,%1 ;"
+                          "      xor. %0,%3,%0;"
+                          "      bne 1f;"
+                          "      stwcx. %2,0,%1;"
+                          "      bne- 0b;"
+                          "1:    "
+                          : "=&r" (ret)
+                          : "r" (p), "r" (1), "r" (0)
+                          : "cr0", "memory");
+    return ret;
+}
+#endif
+
+#ifdef __i386__
+static inline int testandset (int *p)
+{
+    char ret;
+    long int readval;
+    
+    __asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
+                          : "=q" (ret), "=m" (*p), "=a" (readval)
+                          : "r" (1), "m" (*p), "a" (0)
+                          : "memory");
+    return ret;
+}
+#endif
+
+int global_cpu_lock = 0;
+
+void cpu_lock(void)
+{
+    while (testandset(&global_cpu_lock));
+}
+
+void cpu_unlock(void)
+{
+    global_cpu_lock = 0;
+}
+
 #ifdef DEBUG_EXEC
 static const char *cc_op_str[] = {
     "DYNAMIC",
@@ -266,11 +312,15 @@ int cpu_x86_exec(CPUX86State *env1)
             tc_ptr = tb->tc_ptr;
             if (!tb->tc_ptr) {
                 /* if no translated code available, then translate it now */
+                /* XXX: very inefficient: we lock all the cpus when
+                   generating code */
+                cpu_lock();
                 tc_ptr = code_gen_ptr;
                 cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, 
                                  &code_gen_size, pc, cs_base, flags);
                 tb->tc_ptr = tc_ptr;
                 code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
+                cpu_unlock();
             }
             /* execute the generated code */
             gen_func = (void *)tc_ptr;
index f2e1386b5758c5eeb06e6c2af36472ae7e2ac01d..0bcfd22dfcb83a0b5e73d379a44bd55c33befd02 100644 (file)
@@ -139,3 +139,5 @@ typedef struct CCTable {
 extern CCTable cc_table[];
 
 void load_seg(int seg_reg, int selector);
+void cpu_lock(void);
+void cpu_unlock(void);
index 3222629b27badf2568a73309fe56129f9b71d566..cd08c474c458ea386e76403b1bec1d679b365150 100644 (file)
@@ -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;
 }
index 4f09e6fde217cf0e79dfc3c9f915c44278925ebf..ae86176c35bdc6691a714abf517e81d102fadc37 100644 (file)
@@ -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
index afdf1896763c5fdbd877a13d2c9c7b218baafa98..da54a0a186819ee6ae5bd085c98c00402680cf9b 100644 (file)
@@ -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:
index 8b2d6bd75635cdb70705f21fcb55787748f67b1b..6b0a714cb7b924fc19ba80a9a7360bf664a8e49e 100644 (file)
@@ -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 */
index a9583ecf394073ca544e003ae9a1643f2ed86f17..835a1ed51bc8ebd7225c9e78ced43b115a70beef 100644 (file)
--- a/op-i386.c
+++ b/op-i386.c
@@ -2272,3 +2272,14 @@ void OPPROTO op_fninit(void)
     env->fptags[6] = 1;
     env->fptags[7] = 1;
 }
+
+/* threading support */
+void OPPROTO op_lock(void)
+{
+    cpu_lock();
+}
+
+void OPPROTO op_unlock(void)
+{
+    cpu_unlock();
+}
index 09290446864f5d4467e856816202f8a19cc99d06..9d9f8471b5569ea3c594a06096d2854f56d9d864 100644 (file)
@@ -530,3 +530,5 @@ DEF(fnstcw_A0)
 DEF(fldcw_A0)
 DEF(fclex)
 DEF(fninit)
+DEF(lock)
+DEF(unlock)
index 3cc205d5b215a128fd46eb0f8269199ab1c27f2c..33623843d5992f4786eb26506b3cb7fa92759bf0 100644 (file)
@@ -4,7 +4,7 @@ CFLAGS=-Wall -O2 -g
 LDFLAGS=
 
 ifeq ($(ARCH),i386)
-TESTS=test2 sha1-i386 test-i386
+TESTS=testclone testsig testthread sha1-i386 test-i386
 endif
 TESTS+=sha1
 
@@ -16,9 +16,15 @@ hello: hello.c
        $(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
        strip hello
 
-test2: test2.c
+testclone: testclone.c
        $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
 
+testsig: testsig.c
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
+
+testthread: testthread.c
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread
+
 # i386 emulation test (test various opcodes) */
 test-i386: test-i386.c test-i386-code16.S \
            test-i386.h test-i386-shift.h test-i386-muldiv.h
diff --git a/tests/testclone.c b/tests/testclone.c
new file mode 100644 (file)
index 0000000..2152dfc
--- /dev/null
@@ -0,0 +1,61 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <sched.h>
+
+int thread1_func(void *arg)
+{
+    int i;
+    char buf[512];
+
+    for(i=0;i<10;i++) {
+        snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
+        write(1, buf, strlen(buf));
+        usleep(100 * 1000);
+    }
+    return 0;
+}
+
+int thread2_func(void *arg)
+{
+    int i;
+    char buf[512];
+    for(i=0;i<20;i++) {
+        snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
+        write(1, buf, strlen(buf));
+        usleep(120 * 1000);
+    }
+    return 0;
+}
+
+#define STACK_SIZE 16384
+
+void test_clone(void)
+{
+    uint8_t *stack1, *stack2;
+    int pid1, pid2, status1, status2;
+
+    stack1 = malloc(STACK_SIZE);
+    pid1 = clone(thread1_func, stack1 + STACK_SIZE, 
+                 CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1");
+
+    stack2 = malloc(STACK_SIZE);
+    pid2 = clone(thread2_func, stack2 + STACK_SIZE, 
+                 CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2");
+
+    while (waitpid(pid1, &status1, 0) != pid1);
+    while (waitpid(pid2, &status2, 0) != pid2);
+    printf("status1=0x%x\n", status1);
+    printf("status2=0x%x\n", status2);
+    printf("End of clone test.\n");
+}
+
+int main(int argc, char **argv)
+{
+    test_clone();
+    return 0;
+}
diff --git a/tests/testsig.c b/tests/testsig.c
new file mode 100644 (file)
index 0000000..59af54f
--- /dev/null
@@ -0,0 +1,24 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+
+void alarm_handler(int sig)
+{
+    printf("alarm signal=%d\n", sig);
+    alarm(1);
+}
+
+int main(int argc, char **argv)
+{
+    struct sigaction act;
+    act.sa_handler = alarm_handler;
+    sigemptyset(&act.sa_mask);
+    act.sa_flags = 0;
+    sigaction(SIGALRM, &act, NULL);
+    alarm(1);
+    for(;;) {
+        sleep(1);
+    }
+    return 0;
+}
diff --git a/tests/testthread.c b/tests/testthread.c
new file mode 100644 (file)
index 0000000..9a590db
--- /dev/null
@@ -0,0 +1,50 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <signal.h>
+#include <unistd.h>
+#include <inttypes.h>
+#include <pthread.h>
+#include <sys/wait.h>
+#include <sched.h>
+
+void *thread1_func(void *arg)
+{
+    int i;
+    char buf[512];
+
+    for(i=0;i<10;i++) {
+        snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
+        write(1, buf, strlen(buf));
+        usleep(100 * 1000);
+    }
+    return NULL;
+}
+
+void *thread2_func(void *arg)
+{
+    int i;
+    char buf[512];
+    for(i=0;i<20;i++) {
+        snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
+        write(1, buf, strlen(buf));
+        usleep(150 * 1000);
+    }
+    return NULL;
+}
+
+void test_pthread(void)
+{
+    pthread_t tid1, tid2;
+
+    pthread_create(&tid1, NULL, thread1_func, "hello1");
+    pthread_create(&tid2, NULL, thread2_func, "hello2");
+    pthread_join(tid1, NULL);
+    pthread_join(tid2, NULL);
+    printf("End of pthread test.\n");
+}
+
+int main(int argc, char **argv)
+{
+    test_pthread();
+    return 0;
+}
index 5cbaa813cef55b18a5ab8eaaf773711ba74d333e..b7a7cdc207c7d1fae3113f93ed87f48917d2f715 100644 (file)
@@ -1395,6 +1395,10 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
     s->aflag = aflag;
     s->dflag = dflag;
 
+    /* lock generation */
+    if (prefixes & PREFIX_LOCK)
+        gen_op_lock();
+
     /* now check op code */
  reswitch:
     switch(b) {
@@ -3153,8 +3157,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
     default:
         goto illegal_op;
     }
+    /* lock generation */
+    if (s->prefix & PREFIX_LOCK)
+        gen_op_unlock();
     return (long)s->pc;
  illegal_op:
+    /* XXX: ensure that no lock was generated */
     return -1;
 }
 
@@ -3609,6 +3617,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
             pc += count;
         }
         fprintf(logfile, "\n");
+        fflush(logfile);
     }
 #endif
     return 0;