basic clone() support

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162
This commit is contained in:
bellard 2003-03-22 17:31:38 +00:00
parent 612384d771
commit 1b6b029e40
14 changed files with 327 additions and 44 deletions

9
TODO
View File

@ -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)

View 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;

View 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);

View 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;
}

View 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

View 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:

View 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 */

View File

@ -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();
}

View File

@ -530,3 +530,5 @@ DEF(fnstcw_A0)
DEF(fldcw_A0)
DEF(fclex)
DEF(fninit)
DEF(lock)
DEF(unlock)

View 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

61
tests/testclone.c Normal file
View File

@ -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;
}

24
tests/testsig.c Normal file
View File

@ -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;
}

50
tests/testthread.c Normal file
View File

@ -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;
}

View 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;