aboutsummaryrefslogtreecommitdiff
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
parent612384d77146639cebdc9b71c87ee4a94bf44501 (diff)
basic clone() support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162
-rw-r--r--TODO9
-rw-r--r--exec-i386.c50
-rw-r--r--exec-i386.h2
-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
-rw-r--r--op-i386.c11
-rw-r--r--opc-i386.h2
-rw-r--r--tests/Makefile10
-rw-r--r--tests/testclone.c61
-rw-r--r--tests/testsig.c24
-rw-r--r--tests/testthread.c50
-rw-r--r--translate-i386.c9
14 files changed, 327 insertions, 44 deletions
diff --git a/TODO b/TODO
index 36efe4e9e4..5c7c963df3 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 e83d220369..573ba0a8c6 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 f2e1386b57..0bcfd22dfc 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 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 */
diff --git a/op-i386.c b/op-i386.c
index a9583ecf39..835a1ed51b 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 0929044686..9d9f8471b5 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 3cc205d5b2..33623843d5 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 0000000000..2152dfcf23
--- /dev/null
+++ b/tests/testclone.c
@@ -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
index 0000000000..59af54fc8e
--- /dev/null
+++ b/tests/testsig.c
@@ -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
index 0000000000..9a590dbd4f
--- /dev/null
+++ b/tests/testthread.c
@@ -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;
+}
diff --git a/translate-i386.c b/translate-i386.c
index 5cbaa813ce..b7a7cdc207 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;