From 1b6b029e40c4297ce9c27e0f8b8ae177085c990a Mon Sep 17 00:00:00 2001 From: bellard Date: Sat, 22 Mar 2003 17:31:38 +0000 Subject: [PATCH] basic clone() support git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162 --- TODO | 9 ++++--- exec-i386.c | 50 ++++++++++++++++++++++++++++++++++ exec-i386.h | 2 ++ linux-user/main.c | 68 +++++++++++++++++++++++++---------------------- linux-user/qemu.h | 4 +-- linux-user/syscall.c | 66 ++++++++++++++++++++++++++++++++++++++++++--- linux-user/syscall_defs.h | 5 ++++ op-i386.c | 11 ++++++++ opc-i386.h | 2 ++ tests/Makefile | 10 +++++-- tests/testclone.c | 61 ++++++++++++++++++++++++++++++++++++++++++ tests/testsig.c | 24 +++++++++++++++++ tests/testthread.c | 50 ++++++++++++++++++++++++++++++++++ translate-i386.c | 9 +++++++ 14 files changed, 327 insertions(+), 44 deletions(-) create mode 100644 tests/testclone.c create mode 100644 tests/testsig.c create mode 100644 tests/testthread.c diff --git a/TODO b/TODO index 36efe4e..5c7c963 100644 --- 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) diff --git a/exec-i386.c b/exec-i386.c index e83d220..573ba0a 100644 --- a/exec-i386.c +++ b/exec-i386.c @@ -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; diff --git a/exec-i386.h b/exec-i386.h index f2e1386..0bcfd22 100644 --- a/exec-i386.h +++ b/exec-i386.h @@ -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); diff --git a/linux-user/main.c b/linux-user/main.c index 3222629..cd08c47 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 4f09e6f..ae86176 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 afdf189..da54a0a 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 8b2d6bd..6b0a714 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 */ diff --git a/op-i386.c b/op-i386.c index a9583ec..835a1ed 100644 --- 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(); +} diff --git a/opc-i386.h b/opc-i386.h index 0929044..9d9f847 100644 --- a/opc-i386.h +++ b/opc-i386.h @@ -530,3 +530,5 @@ DEF(fnstcw_A0) DEF(fldcw_A0) DEF(fclex) DEF(fninit) +DEF(lock) +DEF(unlock) diff --git a/tests/Makefile b/tests/Makefile index 3cc205d..3362384 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -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 index 0000000..2152dfc --- /dev/null +++ b/tests/testclone.c @@ -0,0 +1,61 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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 index 0000000..59af54f --- /dev/null +++ b/tests/testsig.c @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +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 index 0000000..9a590db --- /dev/null +++ b/tests/testthread.c @@ -0,0 +1,50 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/translate-i386.c b/translate-i386.c index 5cbaa81..b7a7cdc 100644 --- a/translate-i386.c +++ b/translate-i386.c @@ -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; -- 2.7.4