Skip to content

Commit

Permalink
basic clone() support
Browse files Browse the repository at this point in the history
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162
  • Loading branch information
bellard committed Mar 22, 2003
1 parent 612384d commit 1b6b029
Show file tree
Hide file tree
Showing 14 changed files with 327 additions and 44 deletions.
9 changes: 5 additions & 4 deletions TODO
Original file line number Diff line number Diff line change
@@ -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)
50 changes: 50 additions & 0 deletions exec-i386.c
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions exec-i386.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
68 changes: 36 additions & 32 deletions linux-user/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -113,8 +147,6 @@ void usage(void)
exit(1);
}



int main(int argc, char **argv)
{
const char *filename;
Expand Down Expand Up @@ -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;
}
4 changes: 2 additions & 2 deletions linux-user/qemu.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
66 changes: 62 additions & 4 deletions linux-user/syscall.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand All @@ -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:
{
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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:
Expand Down
5 changes: 5 additions & 0 deletions linux-user/syscall_defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down
11 changes: 11 additions & 0 deletions op-i386.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
2 changes: 2 additions & 0 deletions opc-i386.h
Original file line number Diff line number Diff line change
Expand Up @@ -530,3 +530,5 @@ DEF(fnstcw_A0)
DEF(fldcw_A0)
DEF(fclex)
DEF(fninit)
DEF(lock)
DEF(unlock)
10 changes: 8 additions & 2 deletions tests/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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
Expand Down
Loading

0 comments on commit 1b6b029

Please sign in to comment.