Commit 1b6b029e40c4297ce9c27e0f8b8ae177085c990a

Authored by bellard
1 parent 612384d7

basic clone() support


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162
1   -- overrides/16bit for string ops
2   -- optimize translated cache chaining (DLL PLT-like system)
3   -- 64 bit syscalls
  1 +- verify thread support (clone() and various locks)
4 2 - signals
5   -- threads
  3 +- optimize translated cache chaining (DLL PLT-like system)
  4 +- vm86 syscall support
  5 +- overrides/16bit for string ops
  6 +- more syscalls (in particular all 64 bit ones)
6 7 - make it self runnable (use same trick as ld.so : include its own relocator and libc)
7 8 - improved 16 bit support
8 9 - fix FPU exceptions (in particular: gen_op_fpush not before mem load)
... ...
exec-i386.c
... ... @@ -52,6 +52,52 @@ int nb_tbs;
52 52 uint8_t code_gen_buffer[CODE_GEN_BUFFER_SIZE];
53 53 uint8_t *code_gen_ptr;
54 54  
  55 +/* thread support */
  56 +
  57 +#ifdef __powerpc__
  58 +static inline int testandset (int *p)
  59 +{
  60 + int ret;
  61 + __asm__ __volatile__ (
  62 + "0: lwarx %0,0,%1 ;"
  63 + " xor. %0,%3,%0;"
  64 + " bne 1f;"
  65 + " stwcx. %2,0,%1;"
  66 + " bne- 0b;"
  67 + "1: "
  68 + : "=&r" (ret)
  69 + : "r" (p), "r" (1), "r" (0)
  70 + : "cr0", "memory");
  71 + return ret;
  72 +}
  73 +#endif
  74 +
  75 +#ifdef __i386__
  76 +static inline int testandset (int *p)
  77 +{
  78 + char ret;
  79 + long int readval;
  80 +
  81 + __asm__ __volatile__ ("lock; cmpxchgl %3, %1; sete %0"
  82 + : "=q" (ret), "=m" (*p), "=a" (readval)
  83 + : "r" (1), "m" (*p), "a" (0)
  84 + : "memory");
  85 + return ret;
  86 +}
  87 +#endif
  88 +
  89 +int global_cpu_lock = 0;
  90 +
  91 +void cpu_lock(void)
  92 +{
  93 + while (testandset(&global_cpu_lock));
  94 +}
  95 +
  96 +void cpu_unlock(void)
  97 +{
  98 + global_cpu_lock = 0;
  99 +}
  100 +
55 101 #ifdef DEBUG_EXEC
56 102 static const char *cc_op_str[] = {
57 103 "DYNAMIC",
... ... @@ -266,11 +312,15 @@ int cpu_x86_exec(CPUX86State *env1)
266 312 tc_ptr = tb->tc_ptr;
267 313 if (!tb->tc_ptr) {
268 314 /* if no translated code available, then translate it now */
  315 + /* XXX: very inefficient: we lock all the cpus when
  316 + generating code */
  317 + cpu_lock();
269 318 tc_ptr = code_gen_ptr;
270 319 cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE,
271 320 &code_gen_size, pc, cs_base, flags);
272 321 tb->tc_ptr = tc_ptr;
273 322 code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1));
  323 + cpu_unlock();
274 324 }
275 325 /* execute the generated code */
276 326 gen_func = (void *)tc_ptr;
... ...
exec-i386.h
... ... @@ -139,3 +139,5 @@ typedef struct CCTable {
139 139 extern CCTable cc_table[];
140 140  
141 141 void load_seg(int seg_reg, int selector);
  142 +void cpu_lock(void);
  143 +void cpu_unlock(void);
... ...
linux-user/main.c
... ... @@ -104,6 +104,40 @@ void write_dt(void *ptr, unsigned long addr, unsigned long limit,
104 104  
105 105 uint64_t gdt_table[6];
106 106  
  107 +void cpu_loop(struct CPUX86State *env)
  108 +{
  109 + for(;;) {
  110 + int err;
  111 + uint8_t *pc;
  112 +
  113 + err = cpu_x86_exec(env);
  114 + pc = env->seg_cache[R_CS].base + env->eip;
  115 + switch(err) {
  116 + case EXCP0D_GPF:
  117 + if (pc[0] == 0xcd && pc[1] == 0x80) {
  118 + /* syscall */
  119 + env->eip += 2;
  120 + env->regs[R_EAX] = do_syscall(env,
  121 + env->regs[R_EAX],
  122 + env->regs[R_EBX],
  123 + env->regs[R_ECX],
  124 + env->regs[R_EDX],
  125 + env->regs[R_ESI],
  126 + env->regs[R_EDI],
  127 + env->regs[R_EBP]);
  128 + } else {
  129 + goto trap_error;
  130 + }
  131 + break;
  132 + default:
  133 + trap_error:
  134 + fprintf(stderr, "0x%08lx: Unknown exception %d, aborting\n",
  135 + (long)pc, err);
  136 + abort();
  137 + }
  138 + }
  139 +}
  140 +
107 141 void usage(void)
108 142 {
109 143 printf("gemu version " GEMU_VERSION ", Copyright (c) 2003 Fabrice Bellard\n"
... ... @@ -113,8 +147,6 @@ void usage(void)
113 147 exit(1);
114 148 }
115 149  
116   -
117   -
118 150 int main(int argc, char **argv)
119 151 {
120 152 const char *filename;
... ... @@ -193,35 +225,7 @@ int main(int argc, char **argv)
193 225 cpu_x86_load_seg(env, R_FS, __USER_DS);
194 226 cpu_x86_load_seg(env, R_GS, __USER_DS);
195 227  
196   - for(;;) {
197   - int err;
198   - uint8_t *pc;
199   -
200   - err = cpu_x86_exec(env);
201   - pc = env->seg_cache[R_CS].base + env->eip;
202   - switch(err) {
203   - case EXCP0D_GPF:
204   - if (pc[0] == 0xcd && pc[1] == 0x80) {
205   - /* syscall */
206   - env->eip += 2;
207   - env->regs[R_EAX] = do_syscall(env,
208   - env->regs[R_EAX],
209   - env->regs[R_EBX],
210   - env->regs[R_ECX],
211   - env->regs[R_EDX],
212   - env->regs[R_ESI],
213   - env->regs[R_EDI],
214   - env->regs[R_EBP]);
215   - } else {
216   - goto trap_error;
217   - }
218   - break;
219   - default:
220   - trap_error:
221   - fprintf(stderr, "0x%08lx: Unknown exception %d, aborting\n",
222   - (long)pc, err);
223   - abort();
224   - }
225   - }
  228 + cpu_loop(env);
  229 + /* never exits */
226 230 return 0;
227 231 }
... ...
linux-user/qemu.h
... ... @@ -51,7 +51,7 @@ void syscall_init(void);
51 51 long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
52 52 long arg4, long arg5, long arg6);
53 53 void gemu_log(const char *fmt, ...) __attribute__((format(printf,1,2)));
54   -
55   -
  54 +struct CPUX86State;
  55 +void cpu_loop(struct CPUX86State *env);
56 56  
57 57 #endif
... ...
linux-user/syscall.c
... ... @@ -762,8 +762,48 @@ int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecou
762 762 }
763 763 return ret;
764 764 }
  765 +
  766 +/* this stack is the equivalent of the kernel stack associated with a
  767 + thread/process */
  768 +#define NEW_STACK_SIZE 8192
  769 +
  770 +static int clone_func(void *arg)
  771 +{
  772 + CPUX86State *env = arg;
  773 + cpu_loop(env);
  774 + /* never exits */
  775 + return 0;
  776 +}
  777 +
  778 +int do_fork(CPUX86State *env, unsigned int flags, unsigned long newsp)
  779 +{
  780 + int ret;
  781 + uint8_t *new_stack;
  782 + CPUX86State *new_env;
  783 +
  784 + if (flags & CLONE_VM) {
  785 + if (!newsp)
  786 + newsp = env->regs[R_ESP];
  787 + new_stack = malloc(NEW_STACK_SIZE);
  788 +
  789 + /* we create a new CPU instance. */
  790 + new_env = cpu_x86_init();
  791 + memcpy(new_env, env, sizeof(CPUX86State));
  792 + new_env->regs[R_ESP] = newsp;
  793 + new_env->regs[R_EAX] = 0;
  794 + ret = clone(clone_func, new_stack + NEW_STACK_SIZE, flags, new_env);
  795 + } else {
  796 + /* if no CLONE_VM, we consider it is a fork */
  797 + if ((flags & ~CSIGNAL) != 0)
  798 + return -EINVAL;
  799 + ret = fork();
  800 + }
  801 + return ret;
  802 +}
  803 +
765 804 #endif
766 805  
  806 +
767 807 void syscall_init(void)
768 808 {
769 809 #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 828 #ifdef HAVE_GPROF
789 829 _mcleanup();
790 830 #endif
  831 + /* XXX: should free thread stack and CPU env */
791 832 _exit(arg1);
792 833 ret = 0; /* avoid warning */
793 834 break;
... ... @@ -807,7 +848,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
807 848 ret = do_brk((char *)arg1);
808 849 break;
809 850 case TARGET_NR_fork:
810   - ret = get_errno(fork());
  851 + ret = get_errno(do_fork(cpu_env, SIGCHLD, 0));
811 852 break;
812 853 case TARGET_NR_waitpid:
813 854 {
... ... @@ -1241,7 +1282,8 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
1241 1282 case TARGET_NR_sigreturn:
1242 1283 goto unimplemented;
1243 1284 case TARGET_NR_clone:
1244   - goto unimplemented;
  1285 + ret = get_errno(do_fork(cpu_env, arg1, arg2));
  1286 + break;
1245 1287 case TARGET_NR_setdomainname:
1246 1288 ret = get_errno(setdomainname((const char *)arg1, arg2));
1247 1289 break;
... ... @@ -1310,7 +1352,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
1310 1352 case TARGET_NR_sysfs:
1311 1353 goto unimplemented;
1312 1354 case TARGET_NR_personality:
1313   - ret = get_errno(mprotect((void *)arg1, arg2, arg3));
  1355 + ret = get_errno(personality(arg1));
1314 1356 break;
1315 1357 case TARGET_NR_afs_syscall:
1316 1358 goto unimplemented;
... ... @@ -1447,7 +1489,23 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
1447 1489 case TARGET_NR_sched_get_priority_max:
1448 1490 case TARGET_NR_sched_get_priority_min:
1449 1491 case TARGET_NR_sched_rr_get_interval:
  1492 + goto unimplemented;
  1493 +
1450 1494 case TARGET_NR_nanosleep:
  1495 + {
  1496 + struct target_timespec *target_req = (void *)arg1;
  1497 + struct target_timespec *target_rem = (void *)arg2;
  1498 + struct timespec req, rem;
  1499 + req.tv_sec = tswapl(target_req->tv_sec);
  1500 + req.tv_nsec = tswapl(target_req->tv_nsec);
  1501 + ret = get_errno(nanosleep(&req, &rem));
  1502 + if (target_rem) {
  1503 + target_rem->tv_sec = tswapl(rem.tv_sec);
  1504 + target_rem->tv_nsec = tswapl(rem.tv_nsec);
  1505 + }
  1506 + }
  1507 + break;
  1508 +
1451 1509 case TARGET_NR_mremap:
1452 1510 case TARGET_NR_setresuid:
1453 1511 case TARGET_NR_getresuid:
... ... @@ -1481,7 +1539,7 @@ long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3,
1481 1539 case TARGET_NR_getpmsg:
1482 1540 case TARGET_NR_putpmsg:
1483 1541 case TARGET_NR_vfork:
1484   - ret = get_errno(vfork());
  1542 + ret = get_errno(do_fork(cpu_env, CLONE_VFORK | CLONE_VM | SIGCHLD, 0));
1485 1543 break;
1486 1544 case TARGET_NR_ugetrlimit:
1487 1545 case TARGET_NR_truncate64:
... ...
linux-user/syscall_defs.h
... ... @@ -24,6 +24,11 @@ struct target_timeval {
24 24 target_long tv_usec;
25 25 };
26 26  
  27 +struct target_timespec {
  28 + target_long tv_sec;
  29 + target_long tv_nsec;
  30 +};
  31 +
27 32 struct target_iovec {
28 33 target_long iov_base; /* Starting address */
29 34 target_long iov_len; /* Number of bytes */
... ...
op-i386.c
... ... @@ -2272,3 +2272,14 @@ void OPPROTO op_fninit(void)
2272 2272 env->fptags[6] = 1;
2273 2273 env->fptags[7] = 1;
2274 2274 }
  2275 +
  2276 +/* threading support */
  2277 +void OPPROTO op_lock(void)
  2278 +{
  2279 + cpu_lock();
  2280 +}
  2281 +
  2282 +void OPPROTO op_unlock(void)
  2283 +{
  2284 + cpu_unlock();
  2285 +}
... ...
opc-i386.h
... ... @@ -530,3 +530,5 @@ DEF(fnstcw_A0)
530 530 DEF(fldcw_A0)
531 531 DEF(fclex)
532 532 DEF(fninit)
  533 +DEF(lock)
  534 +DEF(unlock)
... ...
tests/Makefile
... ... @@ -4,7 +4,7 @@ CFLAGS=-Wall -O2 -g
4 4 LDFLAGS=
5 5  
6 6 ifeq ($(ARCH),i386)
7   -TESTS=test2 sha1-i386 test-i386
  7 +TESTS=testclone testsig testthread sha1-i386 test-i386
8 8 endif
9 9 TESTS+=sha1
10 10  
... ... @@ -16,9 +16,15 @@ hello: hello.c
16 16 $(CC) -nostdlib $(CFLAGS) -static $(LDFLAGS) -o $@ $<
17 17 strip hello
18 18  
19   -test2: test2.c
  19 +testclone: testclone.c
20 20 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
21 21  
  22 +testsig: testsig.c
  23 + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<
  24 +
  25 +testthread: testthread.c
  26 + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< -lpthread
  27 +
22 28 # i386 emulation test (test various opcodes) */
23 29 test-i386: test-i386.c test-i386-code16.S \
24 30 test-i386.h test-i386-shift.h test-i386-muldiv.h
... ...
tests/testclone.c 0 → 100644
  1 +#include <stdlib.h>
  2 +#include <stdio.h>
  3 +#include <signal.h>
  4 +#include <unistd.h>
  5 +#include <inttypes.h>
  6 +#include <pthread.h>
  7 +#include <sys/wait.h>
  8 +#include <sched.h>
  9 +
  10 +int thread1_func(void *arg)
  11 +{
  12 + int i;
  13 + char buf[512];
  14 +
  15 + for(i=0;i<10;i++) {
  16 + snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
  17 + write(1, buf, strlen(buf));
  18 + usleep(100 * 1000);
  19 + }
  20 + return 0;
  21 +}
  22 +
  23 +int thread2_func(void *arg)
  24 +{
  25 + int i;
  26 + char buf[512];
  27 + for(i=0;i<20;i++) {
  28 + snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
  29 + write(1, buf, strlen(buf));
  30 + usleep(120 * 1000);
  31 + }
  32 + return 0;
  33 +}
  34 +
  35 +#define STACK_SIZE 16384
  36 +
  37 +void test_clone(void)
  38 +{
  39 + uint8_t *stack1, *stack2;
  40 + int pid1, pid2, status1, status2;
  41 +
  42 + stack1 = malloc(STACK_SIZE);
  43 + pid1 = clone(thread1_func, stack1 + STACK_SIZE,
  44 + CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello1");
  45 +
  46 + stack2 = malloc(STACK_SIZE);
  47 + pid2 = clone(thread2_func, stack2 + STACK_SIZE,
  48 + CLONE_VM | CLONE_FS | CLONE_FILES | SIGCHLD, "hello2");
  49 +
  50 + while (waitpid(pid1, &status1, 0) != pid1);
  51 + while (waitpid(pid2, &status2, 0) != pid2);
  52 + printf("status1=0x%x\n", status1);
  53 + printf("status2=0x%x\n", status2);
  54 + printf("End of clone test.\n");
  55 +}
  56 +
  57 +int main(int argc, char **argv)
  58 +{
  59 + test_clone();
  60 + return 0;
  61 +}
... ...
tests/testsig.c 0 → 100644
  1 +#include <stdlib.h>
  2 +#include <stdio.h>
  3 +#include <signal.h>
  4 +#include <unistd.h>
  5 +
  6 +void alarm_handler(int sig)
  7 +{
  8 + printf("alarm signal=%d\n", sig);
  9 + alarm(1);
  10 +}
  11 +
  12 +int main(int argc, char **argv)
  13 +{
  14 + struct sigaction act;
  15 + act.sa_handler = alarm_handler;
  16 + sigemptyset(&act.sa_mask);
  17 + act.sa_flags = 0;
  18 + sigaction(SIGALRM, &act, NULL);
  19 + alarm(1);
  20 + for(;;) {
  21 + sleep(1);
  22 + }
  23 + return 0;
  24 +}
... ...
tests/testthread.c 0 → 100644
  1 +#include <stdlib.h>
  2 +#include <stdio.h>
  3 +#include <signal.h>
  4 +#include <unistd.h>
  5 +#include <inttypes.h>
  6 +#include <pthread.h>
  7 +#include <sys/wait.h>
  8 +#include <sched.h>
  9 +
  10 +void *thread1_func(void *arg)
  11 +{
  12 + int i;
  13 + char buf[512];
  14 +
  15 + for(i=0;i<10;i++) {
  16 + snprintf(buf, sizeof(buf), "thread1: %d %s\n", i, (char *)arg);
  17 + write(1, buf, strlen(buf));
  18 + usleep(100 * 1000);
  19 + }
  20 + return NULL;
  21 +}
  22 +
  23 +void *thread2_func(void *arg)
  24 +{
  25 + int i;
  26 + char buf[512];
  27 + for(i=0;i<20;i++) {
  28 + snprintf(buf, sizeof(buf), "thread2: %d %s\n", i, (char *)arg);
  29 + write(1, buf, strlen(buf));
  30 + usleep(150 * 1000);
  31 + }
  32 + return NULL;
  33 +}
  34 +
  35 +void test_pthread(void)
  36 +{
  37 + pthread_t tid1, tid2;
  38 +
  39 + pthread_create(&tid1, NULL, thread1_func, "hello1");
  40 + pthread_create(&tid2, NULL, thread2_func, "hello2");
  41 + pthread_join(tid1, NULL);
  42 + pthread_join(tid2, NULL);
  43 + printf("End of pthread test.\n");
  44 +}
  45 +
  46 +int main(int argc, char **argv)
  47 +{
  48 + test_pthread();
  49 + return 0;
  50 +}
... ...
translate-i386.c
... ... @@ -1395,6 +1395,10 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
1395 1395 s->aflag = aflag;
1396 1396 s->dflag = dflag;
1397 1397  
  1398 + /* lock generation */
  1399 + if (prefixes & PREFIX_LOCK)
  1400 + gen_op_lock();
  1401 +
1398 1402 /* now check op code */
1399 1403 reswitch:
1400 1404 switch(b) {
... ... @@ -3153,8 +3157,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start)
3153 3157 default:
3154 3158 goto illegal_op;
3155 3159 }
  3160 + /* lock generation */
  3161 + if (s->prefix & PREFIX_LOCK)
  3162 + gen_op_unlock();
3156 3163 return (long)s->pc;
3157 3164 illegal_op:
  3165 + /* XXX: ensure that no lock was generated */
3158 3166 return -1;
3159 3167 }
3160 3168  
... ... @@ -3609,6 +3617,7 @@ int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size,
3609 3617 pc += count;
3610 3618 }
3611 3619 fprintf(logfile, "\n");
  3620 + fflush(logfile);
3612 3621 }
3613 3622 #endif
3614 3623 return 0;
... ...