Commit 1b6b029e40c4297ce9c27e0f8b8ae177085c990a
1 parent
612384d7
basic clone() support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@40 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
14 changed files
with
327 additions
and
44 deletions
TODO
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
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
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; | ... | ... |