提交 1b6b029e 编写于 作者: B bellard

basic clone() support


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162
上级 612384d7
- overrides/16bit for string ops - verify thread support (clone() and various locks)
- optimize translated cache chaining (DLL PLT-like system)
- 64 bit syscalls
- signals - 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) - make it self runnable (use same trick as ld.so : include its own relocator and libc)
- improved 16 bit support - improved 16 bit support
- fix FPU exceptions (in particular: gen_op_fpush not before mem load) - fix FPU exceptions (in particular: gen_op_fpush not before mem load)
...@@ -52,6 +52,52 @@ int nb_tbs; ...@@ -52,6 +52,52 @@ int nb_tbs;
uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE]; uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
uint8_t *code_gen_ptr; 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 #ifdef DEBUG_EXEC
static const char *cc_op_str[] = { static const char *cc_op_str[] = {
"DYNAMIC", "DYNAMIC",
...@@ -266,11 +312,15 @@ int cpu_x86_exec(CPUX86State *env1) ...@@ -266,11 +312,15 @@ int cpu_x86_exec(CPUX86State *env1)
tc_ptr = tb->tc_ptr; tc_ptr = tb->tc_ptr;
if (!tb->tc_ptr) { if (!tb->tc_ptr) {
/* if no translated code available, then translate it now */ /* 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; tc_ptr = code_gen_ptr;
cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
&code_gen_size, pc, cs_base, flags); &code_gen_size, pc, cs_base, flags);
tb->tc_ptr = tc_ptr; 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)); 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 */ /* execute the generated code */
gen_func = (void *)tc_ptr; gen_func = (void *)tc_ptr;
......
...@@ -139,3 +139,5 @@ typedef struct CCTable { ...@@ -139,3 +139,5 @@ typedef struct CCTable {
extern CCTable cc_table[]; extern CCTable cc_table[];
void load_seg(int seg_reg, int selector); void load_seg(int seg_reg, int selector);
void cpu_lock(void);
void cpu_unlock(void);
...@@ -104,6 +104,40 @@ void write_dt(void *ptr, unsigned long addr, unsigned long limit, ...@@ -104,6 +104,40 @@ void write_dt(void *ptr, unsigned long addr, unsigned long limit,
uint64_t gdt_table[6]; 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) void usage(void)
{ {
printf("gemu version " GEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n" printf("gemu version " GEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
...@@ -113,8 +147,6 @@ void usage(void) ...@@ -113,8 +147,6 @@ void usage(void)
exit(1); exit(1);
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
const char *filename; const char *filename;
...@@ -193,35 +225,7 @@ int main(int argc, char **argv) ...@@ -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_FS, __USER_DS);
cpu_x86_load_seg(env, R_GS, __USER_DS); cpu_x86_load_seg(env, R_GS, __USER_DS);
for(;;) { cpu_loop(env);
int err; /* never exits */
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();
}
}
return 0; return 0;
} }
...@@ -51,7 +51,7 @@ void syscall_init(void); ...@@ -51,7 +51,7 @@ void syscall_init(void);
long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
long arg4, long arg5, long arg6); long arg4, long arg5, long arg6);
void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2))); void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
struct CPUX86State;
void cpu_loop(struct CPUX86State *env);
#endif #endif
...@@ -762,8 +762,48 @@ int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecou ...@@ -762,8 +762,48 @@ int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecou
} }
return ret; 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 #endif
void syscall_init(void) void syscall_init(void)
{ {
#define STRUCT(name, list...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def); #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, ...@@ -788,6 +828,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
#ifdef HAVE_GPROF #ifdef HAVE_GPROF
_mcleanup(); _mcleanup();
#endif #endif
/* XXX: should free thread stack and CPU env */
_exit(arg1); _exit(arg1);
ret = 0; /* avoid warning */ ret = 0; /* avoid warning */
break; break;
...@@ -807,7 +848,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ...@@ -807,7 +848,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
ret = do_brk((char *)arg1); ret = do_brk((char *)arg1);
break; break;
case TARGET_NR_fork: case TARGET_NR_fork:
ret = get_errno(fork()); ret = get_errno(do_fork(cpu_env, SIGCHLD, 0));
break; break;
case TARGET_NR_waitpid: case TARGET_NR_waitpid:
{ {
...@@ -1241,7 +1282,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ...@@ -1241,7 +1282,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
case TARGET_NR_sigreturn: case TARGET_NR_sigreturn:
goto unimplemented; goto unimplemented;
case TARGET_NR_clone: case TARGET_NR_clone:
goto unimplemented; ret = get_errno(do_fork(cpu_env, arg1, arg2));
break;
case TARGET_NR_setdomainname: case TARGET_NR_setdomainname:
ret = get_errno(setdomainname((const char *)arg1, arg2)); ret = get_errno(setdomainname((const char *)arg1, arg2));
break; break;
...@@ -1310,7 +1352,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ...@@ -1310,7 +1352,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
case TARGET_NR_sysfs: case TARGET_NR_sysfs:
goto unimplemented; goto unimplemented;
case TARGET_NR_personality: case TARGET_NR_personality:
ret = get_errno(mprotect((void *)arg1, arg2, arg3)); ret = get_errno(personality(arg1));
break; break;
case TARGET_NR_afs_syscall: case TARGET_NR_afs_syscall:
goto unimplemented; goto unimplemented;
...@@ -1447,7 +1489,23 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ...@@ -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_max:
case TARGET_NR_sched_get_priority_min: case TARGET_NR_sched_get_priority_min:
case TARGET_NR_sched_rr_get_interval: case TARGET_NR_sched_rr_get_interval:
goto unimplemented;
case TARGET_NR_nanosleep: 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_mremap:
case TARGET_NR_setresuid: case TARGET_NR_setresuid:
case TARGET_NR_getresuid: case TARGET_NR_getresuid:
...@@ -1481,7 +1539,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, ...@@ -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_getpmsg:
case TARGET_NR_putpmsg: case TARGET_NR_putpmsg:
case TARGET_NR_vfork: case TARGET_NR_vfork:
ret = get_errno(vfork()); ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
break; break;
case TARGET_NR_ugetrlimit: case TARGET_NR_ugetrlimit:
case TARGET_NR_truncate64: case TARGET_NR_truncate64:
......
...@@ -24,6 +24,11 @@ struct target_timeval { ...@@ -24,6 +24,11 @@ struct target_timeval {
target_long tv_usec; target_long tv_usec;
}; };
struct target_timespec {
target_long tv_sec;
target_long tv_nsec;
};
struct target_iovec { struct target_iovec {
target_long iov_base; /* Starting address */ target_long iov_base; /* Starting address */
target_long iov_len; /* Number of bytes */ target_long iov_len; /* Number of bytes */
......
...@@ -2272,3 +2272,14 @@ void OPPROTO op_fninit(void) ...@@ -2272,3 +2272,14 @@ void OPPROTO op_fninit(void)
env->fptags[6] = 1; env->fptags[6] = 1;
env->fptags[7] = 1; env->fptags[7] = 1;
} }
/* threading support */
void OPPROTO op_lock(void)
{
cpu_lock();
}
void OPPROTO op_unlock(void)
{
cpu_unlock();
}
...@@ -530,3 +530,5 @@ DEF(fnstcw_A0) ...@@ -530,3 +530,5 @@ DEF(fnstcw_A0)
DEF(fldcw_A0) DEF(fldcw_A0)
DEF(fclex) DEF(fclex)
DEF(fninit) DEF(fninit)
DEF(lock)
DEF(unlock)
...@@ -4,7 +4,7 @@ CFLAGS=-Wall -O2 -g ...@@ -4,7 +4,7 @@ CFLAGS=-Wall -O2 -g
LDFLAGS= LDFLAGS=
ifeq ($(ARCH),i386) ifeq ($(ARCH),i386)
TESTS=test2 sha1-i386 test-i386 TESTS=testclone testsig testthread sha1-i386 test-i386
endif endif
TESTS+=sha1 TESTS+=sha1
...@@ -16,9 +16,15 @@ hello: hello.c ...@@ -16,9 +16,15 @@ hello: hello.c
$(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $< $(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
strip hello strip hello
test2: test2.c testclone: testclone.c
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(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) */ # i386 emulation test (test various opcodes) */
test-i386: test-i386.c test-i386-code16.S \ test-i386: test-i386.c test-i386-code16.S \
test-i386.h test-i386-shift.h test-i386-muldiv.h test-i386.h test-i386-shift.h test-i386-muldiv.h
......
#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;
}
#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;
}
#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;
}
...@@ -1395,6 +1395,10 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) ...@@ -1395,6 +1395,10 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
s->aflag = aflag; s->aflag = aflag;
s->dflag = dflag; s->dflag = dflag;
/* lock generation */
if (prefixes & PREFIX_LOCK)
gen_op_lock();
/* now check op code */ /* now check op code */
reswitch: reswitch:
switch(b) { switch(b) {
...@@ -3153,8 +3157,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) ...@@ -3153,8 +3157,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
default: default:
goto illegal_op; goto illegal_op;
} }
/* lock generation */
if (s->prefix & PREFIX_LOCK)
gen_op_unlock();
return (long)s->pc; return (long)s->pc;
illegal_op: illegal_op:
/* XXX: ensure that no lock was generated */
return -1; return -1;
} }
...@@ -3609,6 +3617,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, ...@@ -3609,6 +3617,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
pc += count; pc += count;
} }
fprintf(logfile, "\n"); fprintf(logfile, "\n");
fflush(logfile);
} }
#endif #endif
return 0; return 0;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册