Commit 6dbad63eef5947c6c8750e44f408138779b6d0bb
1 parent
27362c82
added minimal segment support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@28 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
12 changed files
with
550 additions
and
103 deletions
TODO
1 | -- daa/das | |
2 | -- optimize translated cache chaining (DLL PLT like system) | |
3 | 1 | - segment ops (minimal LDT/GDT support for wine) |
2 | +- optimize translated cache chaining (DLL PLT like system) | |
4 | 3 | - improved 16 bit support |
5 | 4 | - optimize inverse flags propagation (easy by generating intermediate |
6 | 5 | micro operation array). | ... | ... |
cpu-i386.h
... | ... | @@ -123,6 +123,20 @@ typedef long double CPU86_LDouble; |
123 | 123 | typedef double CPU86_LDouble; |
124 | 124 | #endif |
125 | 125 | |
126 | +typedef struct SegmentCache { | |
127 | + uint8_t *base; | |
128 | + unsigned long limit; | |
129 | + uint8_t seg_32bit; | |
130 | +} SegmentCache; | |
131 | + | |
132 | +typedef struct SegmentDescriptorTable { | |
133 | + uint8_t *base; | |
134 | + unsigned long limit; | |
135 | + /* this is the returned base when reading the register, just to | |
136 | + avoid that the emulated program modifies it */ | |
137 | + unsigned long emu_base; | |
138 | +} SegmentDescriptorTable; | |
139 | + | |
126 | 140 | typedef struct CPUX86State { |
127 | 141 | /* standard registers */ |
128 | 142 | uint32_t regs[8]; |
... | ... | @@ -135,9 +149,6 @@ typedef struct CPUX86State { |
135 | 149 | uint32_t cc_op; |
136 | 150 | int32_t df; /* D flag : 1 if D = 0, -1 if D = 1 */ |
137 | 151 | |
138 | - /* segments */ | |
139 | - uint8_t *segs_base[6]; | |
140 | - | |
141 | 152 | /* FPU state */ |
142 | 153 | unsigned int fpstt; /* top of stack index */ |
143 | 154 | unsigned int fpus; |
... | ... | @@ -145,12 +156,19 @@ typedef struct CPUX86State { |
145 | 156 | uint8_t fptags[8]; /* 0 = valid, 1 = empty */ |
146 | 157 | CPU86_LDouble fpregs[8]; |
147 | 158 | |
148 | - /* segments */ | |
149 | - uint32_t segs[6]; | |
150 | - | |
151 | 159 | /* emulator internal variables */ |
152 | 160 | CPU86_LDouble ft0; |
153 | 161 | |
162 | + /* segments */ | |
163 | + uint32_t segs[6]; /* selector values */ | |
164 | + SegmentCache seg_cache[6]; /* info taken from LDT/GDT */ | |
165 | + SegmentDescriptorTable gdt; | |
166 | + SegmentDescriptorTable ldt; | |
167 | + SegmentDescriptorTable idt; | |
168 | + | |
169 | + /* various CPU modes */ | |
170 | + int vm86; | |
171 | + | |
154 | 172 | /* exception handling */ |
155 | 173 | jmp_buf jmp_env; |
156 | 174 | int exception_index; |
... | ... | @@ -241,9 +259,17 @@ CPUX86State *cpu_x86_init(void); |
241 | 259 | int cpu_x86_exec(CPUX86State *s); |
242 | 260 | void cpu_x86_close(CPUX86State *s); |
243 | 261 | |
262 | +/* needed to load some predefinied segment registers */ | |
263 | +void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector); | |
264 | + | |
244 | 265 | /* internal functions */ |
266 | + | |
267 | +#define GEN_FLAG_CODE32_SHIFT 0 | |
268 | +#define GEN_FLAG_ADDSEG_SHIFT 1 | |
269 | +#define GEN_FLAG_ST_SHIFT 2 | |
245 | 270 | int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
246 | - int *gen_code_size_ptr, uint8_t *pc_start); | |
271 | + int *gen_code_size_ptr, uint8_t *pc_start, | |
272 | + int flags); | |
247 | 273 | void cpu_x86_tblocks_init(void); |
248 | 274 | |
249 | 275 | #endif /* CPU_I386_H */ | ... | ... |
exec-i386.c
... | ... | @@ -36,8 +36,10 @@ |
36 | 36 | #define CODE_GEN_MAX_BLOCKS (CODE_GEN_BUFFER_SIZE / 64) |
37 | 37 | #define CODE_GEN_HASH_BITS 15 |
38 | 38 | #define CODE_GEN_HASH_SIZE (1 << CODE_GEN_HASH_BITS) |
39 | + | |
39 | 40 | typedef struct TranslationBlock { |
40 | 41 | unsigned long pc; /* simulated PC corresponding to this block */ |
42 | + unsigned int flags; /* flags defining in which context the code was generated */ | |
41 | 43 | uint8_t *tc_ptr; /* pointer to the translated code */ |
42 | 44 | struct TranslationBlock *hash_next; /* next matching block */ |
43 | 45 | } TranslationBlock; |
... | ... | @@ -137,7 +139,8 @@ static void tb_flush(void) |
137 | 139 | |
138 | 140 | /* find a translation block in the translation cache. If not found, |
139 | 141 | allocate a new one */ |
140 | -static inline TranslationBlock *tb_find_and_alloc(unsigned long pc) | |
142 | +static inline TranslationBlock *tb_find_and_alloc(unsigned long pc, | |
143 | + unsigned int flags) | |
141 | 144 | { |
142 | 145 | TranslationBlock **ptb, *tb; |
143 | 146 | unsigned int h; |
... | ... | @@ -148,7 +151,7 @@ static inline TranslationBlock *tb_find_and_alloc(unsigned long pc) |
148 | 151 | tb = *ptb; |
149 | 152 | if (!tb) |
150 | 153 | break; |
151 | - if (tb->pc == pc) | |
154 | + if (tb->pc == pc && tb->flags == flags) | |
152 | 155 | return tb; |
153 | 156 | ptb = &tb->hash_next; |
154 | 157 | } |
... | ... | @@ -158,6 +161,7 @@ static inline TranslationBlock *tb_find_and_alloc(unsigned long pc) |
158 | 161 | tb = &tbs[nb_tbs++]; |
159 | 162 | *ptb = tb; |
160 | 163 | tb->pc = pc; |
164 | + tb->flags = flags; | |
161 | 165 | tb->tc_ptr = NULL; |
162 | 166 | tb->hash_next = NULL; |
163 | 167 | return tb; |
... | ... | @@ -171,7 +175,8 @@ int cpu_x86_exec(CPUX86State *env1) |
171 | 175 | void (*gen_func)(void); |
172 | 176 | TranslationBlock *tb; |
173 | 177 | uint8_t *tc_ptr; |
174 | - | |
178 | + unsigned int flags; | |
179 | + | |
175 | 180 | /* first we save global registers */ |
176 | 181 | saved_T0 = T0; |
177 | 182 | saved_T1 = T1; |
... | ... | @@ -187,13 +192,20 @@ int cpu_x86_exec(CPUX86State *env1) |
187 | 192 | cpu_x86_dump_state(); |
188 | 193 | } |
189 | 194 | #endif |
190 | - tb = tb_find_and_alloc((unsigned long)env->pc); | |
195 | + /* we compute the CPU state. We assume it will not | |
196 | + change during the whole generated block. */ | |
197 | + flags = env->seg_cache[R_CS].seg_32bit << GEN_FLAG_CODE32_SHIFT; | |
198 | + flags |= (((unsigned long)env->seg_cache[R_DS].base | | |
199 | + (unsigned long)env->seg_cache[R_ES].base | | |
200 | + (unsigned long)env->seg_cache[R_SS].base) != 0) << | |
201 | + GEN_FLAG_ADDSEG_SHIFT; | |
202 | + tb = tb_find_and_alloc((unsigned long)env->pc, flags); | |
191 | 203 | tc_ptr = tb->tc_ptr; |
192 | 204 | if (!tb->tc_ptr) { |
193 | 205 | /* if no translated code available, then translate it now */ |
194 | 206 | tc_ptr = code_gen_ptr; |
195 | 207 | cpu_x86_gen_code(code_gen_ptr, CODE_GEN_MAX_SIZE, |
196 | - &code_gen_size, (uint8_t *)env->pc); | |
208 | + &code_gen_size, (uint8_t *)env->pc, flags); | |
197 | 209 | tb->tc_ptr = tc_ptr; |
198 | 210 | code_gen_ptr = (void *)(((unsigned long)code_gen_ptr + code_gen_size + CODE_GEN_ALIGN - 1) & ~(CODE_GEN_ALIGN - 1)); |
199 | 211 | } |
... | ... | @@ -211,3 +223,13 @@ int cpu_x86_exec(CPUX86State *env1) |
211 | 223 | env = saved_env; |
212 | 224 | return ret; |
213 | 225 | } |
226 | + | |
227 | +void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector) | |
228 | +{ | |
229 | + CPUX86State *saved_env; | |
230 | + | |
231 | + saved_env = env; | |
232 | + env = s; | |
233 | + load_seg(seg_reg, selector); | |
234 | + env = saved_env; | |
235 | +} | ... | ... |
exec-i386.h
... | ... | @@ -27,6 +27,7 @@ typedef struct FILE FILE; |
27 | 27 | extern FILE *logfile; |
28 | 28 | extern int loglevel; |
29 | 29 | extern int fprintf(FILE *, const char *, ...); |
30 | +extern int printf(const char *, ...); | |
30 | 31 | |
31 | 32 | #ifdef __i386__ |
32 | 33 | register unsigned int T0 asm("ebx"); |
... | ... | @@ -103,3 +104,5 @@ typedef struct CCTable { |
103 | 104 | } CCTable; |
104 | 105 | |
105 | 106 | extern CCTable cc_table[]; |
107 | + | |
108 | +void load_seg(int seg_reg, int selector); | ... | ... |
linux-user/main.c
1 | 1 | /* |
2 | - * emu main | |
2 | + * gemu main | |
3 | 3 | * |
4 | 4 | * Copyright (c) 2003 Fabrice Bellard |
5 | 5 | * |
... | ... | @@ -80,10 +80,28 @@ int cpu_x86_inl(int addr) |
80 | 80 | return 0; |
81 | 81 | } |
82 | 82 | |
83 | +/* default linux values for the selectors */ | |
84 | +#define __USER_CS (0x23) | |
85 | +#define __USER_DS (0x2B) | |
83 | 86 | |
84 | -/* XXX: currently we use LDT entries */ | |
85 | -#define __USER_CS (0x23|4) | |
86 | -#define __USER_DS (0x2B|4) | |
87 | +void write_dt(void *ptr, unsigned long addr, unsigned long limit, | |
88 | + int seg32_bit) | |
89 | +{ | |
90 | + unsigned int e1, e2, limit_in_pages; | |
91 | + limit_in_pages = 0; | |
92 | + if (limit > 0xffff) { | |
93 | + limit = limit >> 12; | |
94 | + limit_in_pages = 1; | |
95 | + } | |
96 | + e1 = (addr << 16) | (limit & 0xffff); | |
97 | + e2 = ((addr >> 16) & 0xff) | (addr & 0xff000000) | (limit & 0x000f0000); | |
98 | + e2 |= limit_in_pages << 23; /* byte granularity */ | |
99 | + e2 |= seg32_bit << 22; /* 32 bit segment */ | |
100 | + stl((uint8_t *)ptr, e1); | |
101 | + stl((uint8_t *)ptr + 4, e2); | |
102 | +} | |
103 | + | |
104 | +uint64_t gdt_table[6]; | |
87 | 105 | |
88 | 106 | void usage(void) |
89 | 107 | { |
... | ... | @@ -94,6 +112,8 @@ void usage(void) |
94 | 112 | exit(1); |
95 | 113 | } |
96 | 114 | |
115 | + | |
116 | + | |
97 | 117 | int main(int argc, char **argv) |
98 | 118 | { |
99 | 119 | const char *filename; |
... | ... | @@ -149,6 +169,7 @@ int main(int argc, char **argv) |
149 | 169 | |
150 | 170 | env = cpu_x86_init(); |
151 | 171 | |
172 | + /* linux register setup */ | |
152 | 173 | env->regs[R_EAX] = regs->eax; |
153 | 174 | env->regs[R_EBX] = regs->ebx; |
154 | 175 | env->regs[R_ECX] = regs->ecx; |
... | ... | @@ -157,23 +178,19 @@ int main(int argc, char **argv) |
157 | 178 | env->regs[R_EDI] = regs->edi; |
158 | 179 | env->regs[R_EBP] = regs->ebp; |
159 | 180 | env->regs[R_ESP] = regs->esp; |
160 | - env->segs[R_CS] = __USER_CS; | |
161 | - env->segs[R_DS] = __USER_DS; | |
162 | - env->segs[R_ES] = __USER_DS; | |
163 | - env->segs[R_SS] = __USER_DS; | |
164 | - env->segs[R_FS] = __USER_DS; | |
165 | - env->segs[R_GS] = __USER_DS; | |
166 | 181 | env->pc = regs->eip; |
167 | 182 | |
168 | -#if 0 | |
169 | - LDT[__USER_CS >> 3].w86Flags = DF_PRESENT | DF_PAGES | DF_32; | |
170 | - LDT[__USER_CS >> 3].dwSelLimit = 0xfffff; | |
171 | - LDT[__USER_CS >> 3].lpSelBase = NULL; | |
172 | - | |
173 | - LDT[__USER_DS >> 3].w86Flags = DF_PRESENT | DF_PAGES | DF_32; | |
174 | - LDT[__USER_DS >> 3].dwSelLimit = 0xfffff; | |
175 | - LDT[__USER_DS >> 3].lpSelBase = NULL; | |
176 | -#endif | |
183 | + /* linux segment setup */ | |
184 | + env->gdt.base = (void *)gdt_table; | |
185 | + env->gdt.limit = sizeof(gdt_table) - 1; | |
186 | + write_dt(&gdt_table[__USER_CS >> 3], 0, 0xffffffff, 1); | |
187 | + write_dt(&gdt_table[__USER_DS >> 3], 0, 0xffffffff, 1); | |
188 | + cpu_x86_load_seg(env, R_CS, __USER_CS); | |
189 | + cpu_x86_load_seg(env, R_DS, __USER_DS); | |
190 | + cpu_x86_load_seg(env, R_ES, __USER_DS); | |
191 | + cpu_x86_load_seg(env, R_SS, __USER_DS); | |
192 | + cpu_x86_load_seg(env, R_FS, __USER_DS); | |
193 | + cpu_x86_load_seg(env, R_GS, __USER_DS); | |
177 | 194 | |
178 | 195 | for(;;) { |
179 | 196 | int err; |
... | ... | @@ -186,7 +203,8 @@ int main(int argc, char **argv) |
186 | 203 | if (pc[0] == 0xcd && pc[1] == 0x80) { |
187 | 204 | /* syscall */ |
188 | 205 | env->pc += 2; |
189 | - env->regs[R_EAX] = do_syscall(env->regs[R_EAX], | |
206 | + env->regs[R_EAX] = do_syscall(env, | |
207 | + env->regs[R_EAX], | |
190 | 208 | env->regs[R_EBX], |
191 | 209 | env->regs[R_ECX], |
192 | 210 | env->regs[R_EDX], | ... | ... |
linux-user/qemu.h
... | ... | @@ -48,7 +48,7 @@ int elf_exec(const char * filename, char ** argv, char ** envp, |
48 | 48 | |
49 | 49 | void target_set_brk(char *new_brk); |
50 | 50 | void syscall_init(void); |
51 | -long do_syscall(int num, long arg1, long arg2, long arg3, | |
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 | 54 | ... | ... |
linux-user/syscall.c
... | ... | @@ -69,6 +69,7 @@ struct dirent { |
69 | 69 | #include "syscall_defs.h" |
70 | 70 | |
71 | 71 | #ifdef TARGET_I386 |
72 | +#include "cpu-i386.h" | |
72 | 73 | #include "syscall-i386.h" |
73 | 74 | #endif |
74 | 75 | |
... | ... | @@ -607,6 +608,124 @@ StructEntry struct_termios_def = { |
607 | 608 | .align = { __alignof__(struct target_termios), __alignof__(struct host_termios) }, |
608 | 609 | }; |
609 | 610 | |
611 | +#ifdef TARGET_I386 | |
612 | + | |
613 | +/* NOTE: there is really one LDT for all the threads */ | |
614 | +uint8_t *ldt_table; | |
615 | + | |
616 | +static int read_ldt(void *ptr, unsigned long bytecount) | |
617 | +{ | |
618 | + int size; | |
619 | + | |
620 | + if (!ldt_table) | |
621 | + return 0; | |
622 | + size = TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE; | |
623 | + if (size > bytecount) | |
624 | + size = bytecount; | |
625 | + memcpy(ptr, ldt_table, size); | |
626 | + return size; | |
627 | +} | |
628 | + | |
629 | +/* XXX: add locking support */ | |
630 | +static int write_ldt(CPUX86State *env, | |
631 | + void *ptr, unsigned long bytecount, int oldmode) | |
632 | +{ | |
633 | + struct target_modify_ldt_ldt_s ldt_info; | |
634 | + int seg_32bit, contents, read_exec_only, limit_in_pages; | |
635 | + int seg_not_present, useable; | |
636 | + uint32_t *lp, entry_1, entry_2; | |
637 | + | |
638 | + if (bytecount != sizeof(ldt_info)) | |
639 | + return -EINVAL; | |
640 | + memcpy(&ldt_info, ptr, sizeof(ldt_info)); | |
641 | + tswap32s(&ldt_info.entry_number); | |
642 | + tswapls((long *)&ldt_info.base_addr); | |
643 | + tswap32s(&ldt_info.limit); | |
644 | + tswap32s(&ldt_info.flags); | |
645 | + | |
646 | + if (ldt_info.entry_number >= TARGET_LDT_ENTRIES) | |
647 | + return -EINVAL; | |
648 | + seg_32bit = ldt_info.flags & 1; | |
649 | + contents = (ldt_info.flags >> 1) & 3; | |
650 | + read_exec_only = (ldt_info.flags >> 3) & 1; | |
651 | + limit_in_pages = (ldt_info.flags >> 4) & 1; | |
652 | + seg_not_present = (ldt_info.flags >> 5) & 1; | |
653 | + useable = (ldt_info.flags >> 6) & 1; | |
654 | + | |
655 | + if (contents == 3) { | |
656 | + if (oldmode) | |
657 | + return -EINVAL; | |
658 | + if (seg_not_present == 0) | |
659 | + return -EINVAL; | |
660 | + } | |
661 | + /* allocate the LDT */ | |
662 | + if (!ldt_table) { | |
663 | + ldt_table = malloc(TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE); | |
664 | + if (!ldt_table) | |
665 | + return -ENOMEM; | |
666 | + memset(ldt_table, 0, TARGET_LDT_ENTRIES * TARGET_LDT_ENTRY_SIZE); | |
667 | + env->ldt.base = ldt_table; | |
668 | + env->ldt.limit = 0xffff; | |
669 | + } | |
670 | + | |
671 | + /* NOTE: same code as Linux kernel */ | |
672 | + /* Allow LDTs to be cleared by the user. */ | |
673 | + if (ldt_info.base_addr == 0 && ldt_info.limit == 0) { | |
674 | + if (oldmode || | |
675 | + (contents == 0 && | |
676 | + read_exec_only == 1 && | |
677 | + seg_32bit == 0 && | |
678 | + limit_in_pages == 0 && | |
679 | + seg_not_present == 1 && | |
680 | + useable == 0 )) { | |
681 | + entry_1 = 0; | |
682 | + entry_2 = 0; | |
683 | + goto install; | |
684 | + } | |
685 | + } | |
686 | + | |
687 | + entry_1 = ((ldt_info.base_addr & 0x0000ffff) << 16) | | |
688 | + (ldt_info.limit & 0x0ffff); | |
689 | + entry_2 = (ldt_info.base_addr & 0xff000000) | | |
690 | + ((ldt_info.base_addr & 0x00ff0000) >> 16) | | |
691 | + (ldt_info.limit & 0xf0000) | | |
692 | + ((read_exec_only ^ 1) << 9) | | |
693 | + (contents << 10) | | |
694 | + ((seg_not_present ^ 1) << 15) | | |
695 | + (seg_32bit << 22) | | |
696 | + (limit_in_pages << 23) | | |
697 | + 0x7000; | |
698 | + if (!oldmode) | |
699 | + entry_2 |= (useable << 20); | |
700 | + | |
701 | + /* Install the new entry ... */ | |
702 | +install: | |
703 | + lp = (uint32_t *)(ldt_table + (ldt_info.entry_number << 3)); | |
704 | + lp[0] = tswap32(entry_1); | |
705 | + lp[1] = tswap32(entry_2); | |
706 | + return 0; | |
707 | +} | |
708 | + | |
709 | +/* specific and weird i386 syscalls */ | |
710 | +int gemu_modify_ldt(CPUX86State *env, int func, void *ptr, unsigned long bytecount) | |
711 | +{ | |
712 | + int ret = -ENOSYS; | |
713 | + | |
714 | + switch (func) { | |
715 | + case 0: | |
716 | + ret = read_ldt(ptr, bytecount); | |
717 | + break; | |
718 | + case 1: | |
719 | + ret = write_ldt(env, ptr, bytecount, 1); | |
720 | + break; | |
721 | + case 0x11: | |
722 | + ret = write_ldt(env, ptr, bytecount, 0); | |
723 | + break; | |
724 | + } | |
725 | + return ret; | |
726 | +} | |
727 | +#endif | |
728 | + | |
610 | 729 | void syscall_init(void) |
611 | 730 | { |
612 | 731 | #define STRUCT(name, list...) thunk_register_struct(STRUCT_ ## name, #name, struct_ ## name ## _def); |
... | ... | @@ -616,7 +735,7 @@ void syscall_init(void) |
616 | 735 | #undef STRUCT_SPECIAL |
617 | 736 | } |
618 | 737 | |
619 | -long do_syscall(int num, long arg1, long arg2, long arg3, | |
738 | +long do_syscall(void *cpu_env, int num, long arg1, long arg2, long arg3, | |
620 | 739 | long arg4, long arg5, long arg6) |
621 | 740 | { |
622 | 741 | long ret; |
... | ... | @@ -1095,8 +1214,11 @@ long do_syscall(int num, long arg1, long arg2, long arg3, |
1095 | 1214 | /* no need to transcode because we use the linux syscall */ |
1096 | 1215 | ret = get_errno(sys_uname((struct new_utsname *)arg1)); |
1097 | 1216 | break; |
1217 | +#ifdef TARGET_I386 | |
1098 | 1218 | case TARGET_NR_modify_ldt: |
1099 | - goto unimplemented; | |
1219 | + ret = get_errno(gemu_modify_ldt(cpu_env, arg1, (void *)arg2, arg3)); | |
1220 | + break; | |
1221 | +#endif | |
1100 | 1222 | case TARGET_NR_adjtimex: |
1101 | 1223 | goto unimplemented; |
1102 | 1224 | case TARGET_NR_mprotect: | ... | ... |
linux-user/syscall_types.h
op-i386.c
... | ... | @@ -858,6 +858,60 @@ void OPPROTO op_das(void) |
858 | 858 | CC_SRC = eflags; |
859 | 859 | } |
860 | 860 | |
861 | +/* segment handling */ | |
862 | + | |
863 | +void load_seg(int seg_reg, int selector) | |
864 | +{ | |
865 | + SegmentCache *sc; | |
866 | + SegmentDescriptorTable *dt; | |
867 | + int index; | |
868 | + uint32_t e1, e2; | |
869 | + uint8_t *ptr; | |
870 | + | |
871 | + env->segs[seg_reg] = selector; | |
872 | + sc = &env->seg_cache[seg_reg]; | |
873 | + if (env->vm86) { | |
874 | + sc->base = (void *)(selector << 4); | |
875 | + sc->limit = 0xffff; | |
876 | + sc->seg_32bit = 0; | |
877 | + } else { | |
878 | + if (selector & 0x4) | |
879 | + dt = &env->ldt; | |
880 | + else | |
881 | + dt = &env->gdt; | |
882 | + index = selector & ~7; | |
883 | + if ((index + 7) > dt->limit) | |
884 | + raise_exception(EXCP0D_GPF); | |
885 | + ptr = dt->base + index; | |
886 | + e1 = ldl(ptr); | |
887 | + e2 = ldl(ptr + 4); | |
888 | + sc->base = (void *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000)); | |
889 | + sc->limit = (e1 & 0xffff) | (e2 & 0x000f0000); | |
890 | + if (e2 & (1 << 23)) | |
891 | + sc->limit = (sc->limit << 12) | 0xfff; | |
892 | + sc->seg_32bit = (e2 >> 22) & 1; | |
893 | +#if 0 | |
894 | + fprintf(logfile, "load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx seg_32bit=%d\n", | |
895 | + selector, (unsigned long)sc->base, sc->limit, sc->seg_32bit); | |
896 | +#endif | |
897 | + } | |
898 | +} | |
899 | + | |
900 | +void OPPROTO op_movl_seg_T0(void) | |
901 | +{ | |
902 | + load_seg(PARAM1, T0 & 0xffff); | |
903 | +} | |
904 | + | |
905 | +void OPPROTO op_movl_T0_seg(void) | |
906 | +{ | |
907 | + T0 = env->segs[PARAM1]; | |
908 | +} | |
909 | + | |
910 | +void OPPROTO op_addl_A0_seg(void) | |
911 | +{ | |
912 | + A0 += *(unsigned long *)((char *)env + PARAM1); | |
913 | +} | |
914 | + | |
861 | 915 | /* flags handling */ |
862 | 916 | |
863 | 917 | /* slow jumps cases (compute x86 flags) */ | ... | ... |
syscall-i386.h
... | ... | @@ -758,3 +758,14 @@ struct target_termios { |
758 | 758 | #define TARGET_SOUND_MIXER_WRITE_ENHANCE 0xc0044d1f |
759 | 759 | #define TARGET_SOUND_MIXER_WRITE_LOUD 0xc0044d1f |
760 | 760 | #define TARGET_SOUND_MIXER_WRITE_RECSRC 0xc0044dff |
761 | + | |
762 | +#define TARGET_LDT_ENTRIES 8192 | |
763 | +#define TARGET_LDT_ENTRY_SIZE 8 | |
764 | + | |
765 | +struct target_modify_ldt_ldt_s { | |
766 | + unsigned int entry_number; | |
767 | + target_ulong base_addr; | |
768 | + unsigned int limit; | |
769 | + unsigned int flags; | |
770 | +}; | |
771 | + | ... | ... |
tests/test-i386.c
1 | 1 | #include <stdlib.h> |
2 | 2 | #include <stdio.h> |
3 | +#include <inttypes.h> | |
3 | 4 | #include <math.h> |
4 | 5 | |
5 | 6 | #define xglue(x, y) x ## y |
... | ... | @@ -612,6 +613,81 @@ void test_bcd(void) |
612 | 613 | TEST_BCD(aad, 0x12340407, CC_A, (CC_C | CC_P | CC_Z | CC_S | CC_O | CC_A)); |
613 | 614 | } |
614 | 615 | |
616 | +/**********************************************/ | |
617 | +/* segmentation tests */ | |
618 | + | |
619 | +#include <asm/ldt.h> | |
620 | +#include <linux/unistd.h> | |
621 | + | |
622 | +_syscall3(int, modify_ldt, int, func, void *, ptr, unsigned long, bytecount) | |
623 | + | |
624 | +uint8_t seg_data1[4096]; | |
625 | +uint8_t seg_data2[4096]; | |
626 | + | |
627 | +#define MK_SEL(n) (((n) << 3) | 4) | |
628 | + | |
629 | +/* NOTE: we use Linux modify_ldt syscall */ | |
630 | +void test_segs(void) | |
631 | +{ | |
632 | + struct modify_ldt_ldt_s ldt; | |
633 | + long long ldt_table[3]; | |
634 | + int i, res, res2; | |
635 | + char tmp; | |
636 | + | |
637 | + ldt.entry_number = 1; | |
638 | + ldt.base_addr = (unsigned long)&seg_data1; | |
639 | + ldt.limit = (sizeof(seg_data1) + 0xfff) >> 12; | |
640 | + ldt.seg_32bit = 1; | |
641 | + ldt.contents = MODIFY_LDT_CONTENTS_DATA; | |
642 | + ldt.read_exec_only = 0; | |
643 | + ldt.limit_in_pages = 1; | |
644 | + ldt.seg_not_present = 0; | |
645 | + ldt.useable = 1; | |
646 | + modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */ | |
647 | + | |
648 | + ldt.entry_number = 2; | |
649 | + ldt.base_addr = (unsigned long)&seg_data2; | |
650 | + ldt.limit = (sizeof(seg_data2) + 0xfff) >> 12; | |
651 | + ldt.seg_32bit = 1; | |
652 | + ldt.contents = MODIFY_LDT_CONTENTS_DATA; | |
653 | + ldt.read_exec_only = 0; | |
654 | + ldt.limit_in_pages = 1; | |
655 | + ldt.seg_not_present = 0; | |
656 | + ldt.useable = 1; | |
657 | + modify_ldt(1, &ldt, sizeof(ldt)); /* write ldt entry */ | |
658 | + | |
659 | + modify_ldt(0, &ldt_table, sizeof(ldt_table)); /* read ldt entries */ | |
660 | + for(i=0;i<3;i++) | |
661 | + printf("%d: %016Lx\n", i, ldt_table[i]); | |
662 | + | |
663 | + /* do some tests with fs or gs */ | |
664 | + asm volatile ("movl %0, %%fs" : : "r" (MK_SEL(1))); | |
665 | + asm volatile ("movl %0, %%gs" : : "r" (MK_SEL(2))); | |
666 | + | |
667 | + seg_data1[1] = 0xaa; | |
668 | + seg_data2[1] = 0x55; | |
669 | + | |
670 | + asm volatile ("fs movzbl 0x1, %0" : "=r" (res)); | |
671 | + printf("FS[1] = %02x\n", res); | |
672 | + | |
673 | + asm volatile ("gs movzbl 0x1, %0" : "=r" (res)); | |
674 | + printf("GS[1] = %02x\n", res); | |
675 | + | |
676 | + /* tests with ds/ss (implicit segment case) */ | |
677 | + tmp = 0xa5; | |
678 | + asm volatile ("pushl %%ebp\n\t" | |
679 | + "pushl %%ds\n\t" | |
680 | + "movl %2, %%ds\n\t" | |
681 | + "movl %3, %%ebp\n\t" | |
682 | + "movzbl 0x1, %0\n\t" | |
683 | + "movzbl (%%ebp), %1\n\t" | |
684 | + "popl %%ds\n\t" | |
685 | + "popl %%ebp\n\t" | |
686 | + : "=r" (res), "=r" (res2) | |
687 | + : "r" (MK_SEL(1)), "r" (&tmp)); | |
688 | + printf("DS[1] = %02x\n", res); | |
689 | + printf("SS[tmp] = %02x\n", res2); | |
690 | +} | |
615 | 691 | |
616 | 692 | static void *call_end __init_call = NULL; |
617 | 693 | |
... | ... | @@ -628,8 +704,9 @@ int main(int argc, char **argv) |
628 | 704 | test_bsx(); |
629 | 705 | test_mul(); |
630 | 706 | test_jcc(); |
631 | - test_lea(); | |
632 | 707 | test_floats(); |
633 | 708 | test_bcd(); |
709 | + test_lea(); | |
710 | + test_segs(); | |
634 | 711 | return 0; |
635 | 712 | } | ... | ... |
translate-i386.c
... | ... | @@ -34,6 +34,10 @@ |
34 | 34 | #include "dis-asm.h" |
35 | 35 | #endif |
36 | 36 | |
37 | +#ifndef offsetof | |
38 | +#define offsetof(type, field) ((size_t) &((type *)0)->field) | |
39 | +#endif | |
40 | + | |
37 | 41 | static uint8_t *gen_code_ptr; |
38 | 42 | int __op_param1, __op_param2, __op_param3; |
39 | 43 | |
... | ... | @@ -71,8 +75,13 @@ typedef struct DisasContext { |
71 | 75 | int prefix; |
72 | 76 | int aflag, dflag; |
73 | 77 | uint8_t *pc; /* current pc */ |
74 | - int cc_op; /* current CC operation */ | |
75 | - int f_st; | |
78 | + int is_jmp; /* 1 = means jump (stop translation), 2 means CPU | |
79 | + static state change (stop translation) */ | |
80 | + /* current block context */ | |
81 | + int code32; /* 32 bit code segment */ | |
82 | + int cc_op; /* current CC operation */ | |
83 | + int addseg; /* non zero if either DS/ES/SS have a non zero base */ | |
84 | + int f_st; /* currently unused */ | |
76 | 85 | } DisasContext; |
77 | 86 | |
78 | 87 | /* i386 arith/logic operations */ |
... | ... | @@ -763,12 +772,32 @@ static void gen_shifti(DisasContext *s1, int op, int ot, int d, int c) |
763 | 772 | static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr) |
764 | 773 | { |
765 | 774 | int havesib; |
766 | - int havebase; | |
767 | 775 | int base, disp; |
768 | - int index = 0; | |
769 | - int scale = 0; | |
770 | - int reg1, reg2, opreg; | |
771 | - int mod, rm, code; | |
776 | + int index; | |
777 | + int scale; | |
778 | + int opreg; | |
779 | + int mod, rm, code, override, must_add_seg; | |
780 | + | |
781 | + /* XXX: add a generation time variable to tell if base == 0 in DS/ES/SS */ | |
782 | + /* XXX: fix lea case */ | |
783 | + override = -1; | |
784 | + must_add_seg = s->addseg; | |
785 | + if (s->prefix & (PREFIX_CS | PREFIX_SS | PREFIX_DS | | |
786 | + PREFIX_ES | PREFIX_FS | PREFIX_GS)) { | |
787 | + if (s->prefix & PREFIX_ES) | |
788 | + override = R_ES; | |
789 | + else if (s->prefix & PREFIX_CS) | |
790 | + override = R_CS; | |
791 | + else if (s->prefix & PREFIX_SS) | |
792 | + override = R_SS; | |
793 | + else if (s->prefix & PREFIX_DS) | |
794 | + override = R_DS; | |
795 | + else if (s->prefix & PREFIX_FS) | |
796 | + override = R_FS; | |
797 | + else | |
798 | + override = R_GS; | |
799 | + must_add_seg = 1; | |
800 | + } | |
772 | 801 | |
773 | 802 | mod = (modrm >> 6) & 3; |
774 | 803 | rm = modrm & 7; |
... | ... | @@ -776,8 +805,9 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ |
776 | 805 | if (s->aflag) { |
777 | 806 | |
778 | 807 | havesib = 0; |
779 | - havebase = 1; | |
780 | 808 | base = rm; |
809 | + index = 0; | |
810 | + scale = 0; | |
781 | 811 | |
782 | 812 | if (base == 4) { |
783 | 813 | havesib = 1; |
... | ... | @@ -790,7 +820,7 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ |
790 | 820 | switch (mod) { |
791 | 821 | case 0: |
792 | 822 | if (base == 5) { |
793 | - havebase = 0; | |
823 | + base = -1; | |
794 | 824 | disp = ldl(s->pc); |
795 | 825 | s->pc += 4; |
796 | 826 | } else { |
... | ... | @@ -806,40 +836,25 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ |
806 | 836 | s->pc += 4; |
807 | 837 | break; |
808 | 838 | } |
809 | - | |
810 | - reg1 = OR_ZERO; | |
811 | - reg2 = OR_ZERO; | |
812 | - | |
813 | - if (havebase || (havesib && (index != 4 || scale != 0))) { | |
814 | - if (havebase) | |
815 | - reg1 = OR_EAX + base; | |
816 | - if (havesib && index != 4) { | |
817 | - if (havebase) | |
818 | - reg2 = index + OR_EAX; | |
819 | - else | |
820 | - reg1 = index + OR_EAX; | |
821 | - } | |
822 | - } | |
823 | - /* XXX: disp only ? */ | |
824 | - if (reg2 == OR_ZERO) { | |
825 | - /* op: disp + (reg1 << scale) */ | |
826 | - if (reg1 == OR_ZERO) { | |
827 | - gen_op_movl_A0_im(disp); | |
828 | - } else if (scale == 0 && disp == 0) { | |
829 | - gen_op_movl_A0_reg[reg1](); | |
830 | - } else { | |
831 | - gen_op_movl_A0_im(disp); | |
832 | - gen_op_addl_A0_reg_sN[scale][reg1](); | |
833 | - } | |
839 | + | |
840 | + if (base >= 0) { | |
841 | + gen_op_movl_A0_reg[base](); | |
842 | + if (disp != 0) | |
843 | + gen_op_addl_A0_im(disp); | |
834 | 844 | } else { |
835 | - /* op: disp + reg1 + (reg2 << scale) */ | |
836 | - if (disp != 0) { | |
837 | - gen_op_movl_A0_im(disp); | |
838 | - gen_op_addl_A0_reg_sN[0][reg1](); | |
839 | - } else { | |
840 | - gen_op_movl_A0_reg[reg1](); | |
845 | + gen_op_movl_A0_im(disp); | |
846 | + } | |
847 | + if (havesib && (index != 4 || scale != 0)) { | |
848 | + gen_op_addl_A0_reg_sN[scale][index](); | |
849 | + } | |
850 | + if (must_add_seg) { | |
851 | + if (override < 0) { | |
852 | + if (base == R_EBP || base == R_ESP) | |
853 | + override = R_SS; | |
854 | + else | |
855 | + override = R_DS; | |
841 | 856 | } |
842 | - gen_op_addl_A0_reg_sN[scale][reg2](); | |
857 | + gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base)); | |
843 | 858 | } |
844 | 859 | } else { |
845 | 860 | switch (mod) { |
... | ... | @@ -848,6 +863,7 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ |
848 | 863 | disp = lduw(s->pc); |
849 | 864 | s->pc += 2; |
850 | 865 | gen_op_movl_A0_im(disp); |
866 | + rm = 0; /* avoid SS override */ | |
851 | 867 | goto no_rm; |
852 | 868 | } else { |
853 | 869 | disp = 0; |
... | ... | @@ -896,8 +912,18 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ |
896 | 912 | if (disp != 0) |
897 | 913 | gen_op_addl_A0_im(disp); |
898 | 914 | gen_op_andl_A0_ffff(); |
899 | - no_rm: ; | |
915 | + no_rm: | |
916 | + if (must_add_seg) { | |
917 | + if (override < 0) { | |
918 | + if (rm == 2 || rm == 3 || rm == 6) | |
919 | + override = R_SS; | |
920 | + else | |
921 | + override = R_DS; | |
922 | + } | |
923 | + gen_op_addl_A0_seg(offsetof(CPUX86State,seg_cache[override].base)); | |
924 | + } | |
900 | 925 | } |
926 | + | |
901 | 927 | opreg = OR_A0; |
902 | 928 | disp = 0; |
903 | 929 | *reg_ptr = opreg; |
... | ... | @@ -1082,10 +1108,19 @@ static void gen_setcc(DisasContext *s, int b) |
1082 | 1108 | } |
1083 | 1109 | } |
1084 | 1110 | |
1111 | +/* move T0 to seg_reg and compute if the CPU state may change */ | |
1112 | +void gen_movl_seg_T0(DisasContext *s, int seg_reg) | |
1113 | +{ | |
1114 | + gen_op_movl_seg_T0(seg_reg); | |
1115 | + if (!s->addseg && seg_reg < R_FS) | |
1116 | + s->is_jmp = 2; /* abort translation because the register may | |
1117 | + have a non zero base */ | |
1118 | +} | |
1119 | + | |
1085 | 1120 | /* return the next pc address. Return -1 if no insn found. *is_jmp_ptr |
1086 | 1121 | is set to true if the instruction sets the PC (last instruction of |
1087 | 1122 | a basic block) */ |
1088 | -long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) | |
1123 | +long disas_insn(DisasContext *s, uint8_t *pc_start) | |
1089 | 1124 | { |
1090 | 1125 | int b, prefixes, aflag, dflag; |
1091 | 1126 | int shift, ot; |
... | ... | @@ -1093,8 +1128,8 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
1093 | 1128 | |
1094 | 1129 | s->pc = pc_start; |
1095 | 1130 | prefixes = 0; |
1096 | - aflag = 1; | |
1097 | - dflag = 1; | |
1131 | + aflag = s->code32; | |
1132 | + dflag = s->code32; | |
1098 | 1133 | // cur_pc = s->pc; /* for insn generation */ |
1099 | 1134 | next_byte: |
1100 | 1135 | b = ldub(s->pc); |
... | ... | @@ -1416,11 +1451,11 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
1416 | 1451 | gen_op_movl_T1_im((long)s->pc); |
1417 | 1452 | gen_op_pushl_T1(); |
1418 | 1453 | gen_op_jmp_T0(); |
1419 | - *is_jmp_ptr = 1; | |
1454 | + s->is_jmp = 1; | |
1420 | 1455 | break; |
1421 | 1456 | case 4: /* jmp Ev */ |
1422 | 1457 | gen_op_jmp_T0(); |
1423 | - *is_jmp_ptr = 1; | |
1458 | + s->is_jmp = 1; | |
1424 | 1459 | break; |
1425 | 1460 | case 6: /* push Ev */ |
1426 | 1461 | gen_op_pushl_T0(); |
... | ... | @@ -1555,6 +1590,30 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
1555 | 1590 | gen_op_popl_T0(); |
1556 | 1591 | gen_op_mov_reg_T0[OT_LONG][R_EBP](); |
1557 | 1592 | break; |
1593 | + case 0x06: /* push es */ | |
1594 | + case 0x0e: /* push cs */ | |
1595 | + case 0x16: /* push ss */ | |
1596 | + case 0x1e: /* push ds */ | |
1597 | + gen_op_movl_T0_seg(b >> 3); | |
1598 | + gen_op_pushl_T0(); | |
1599 | + break; | |
1600 | + case 0x1a0: /* push fs */ | |
1601 | + case 0x1a8: /* push gs */ | |
1602 | + gen_op_movl_T0_seg(((b >> 3) & 7) + R_FS); | |
1603 | + gen_op_pushl_T0(); | |
1604 | + break; | |
1605 | + case 0x07: /* pop es */ | |
1606 | + case 0x17: /* pop ss */ | |
1607 | + case 0x1f: /* pop ds */ | |
1608 | + gen_op_popl_T0(); | |
1609 | + gen_movl_seg_T0(s, b >> 3); | |
1610 | + break; | |
1611 | + case 0x1a1: /* pop fs */ | |
1612 | + case 0x1a9: /* pop gs */ | |
1613 | + gen_op_popl_T0(); | |
1614 | + gen_movl_seg_T0(s, ((b >> 3) & 7) + R_FS); | |
1615 | + break; | |
1616 | + | |
1558 | 1617 | /**************************/ |
1559 | 1618 | /* mov */ |
1560 | 1619 | case 0x88: |
... | ... | @@ -1598,6 +1657,24 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
1598 | 1657 | gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); |
1599 | 1658 | gen_op_mov_reg_T0[ot][reg](); |
1600 | 1659 | break; |
1660 | + case 0x8e: /* mov seg, Gv */ | |
1661 | + ot = dflag ? OT_LONG : OT_WORD; | |
1662 | + modrm = ldub(s->pc++); | |
1663 | + reg = (modrm >> 3) & 7; | |
1664 | + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); | |
1665 | + if (reg >= 6) | |
1666 | + goto illegal_op; | |
1667 | + gen_movl_seg_T0(s, reg); | |
1668 | + break; | |
1669 | + case 0x8c: /* mov Gv, seg */ | |
1670 | + ot = dflag ? OT_LONG : OT_WORD; | |
1671 | + modrm = ldub(s->pc++); | |
1672 | + reg = (modrm >> 3) & 7; | |
1673 | + if (reg >= 6) | |
1674 | + goto illegal_op; | |
1675 | + gen_op_movl_T0_seg(reg); | |
1676 | + gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); | |
1677 | + break; | |
1601 | 1678 | |
1602 | 1679 | case 0x1b6: /* movzbS Gv, Eb */ |
1603 | 1680 | case 0x1b7: /* movzwS Gv, Eb */ |
... | ... | @@ -1648,8 +1725,13 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
1648 | 1725 | ot = dflag ? OT_LONG : OT_WORD; |
1649 | 1726 | modrm = ldub(s->pc++); |
1650 | 1727 | reg = (modrm >> 3) & 7; |
1651 | - | |
1728 | + /* we must ensure that no segment is added */ | |
1729 | + s->prefix &= ~(PREFIX_CS | PREFIX_SS | PREFIX_DS | | |
1730 | + PREFIX_ES | PREFIX_FS | PREFIX_GS); | |
1731 | + val = s->addseg; | |
1732 | + s->addseg = 0; | |
1652 | 1733 | gen_lea_modrm(s, modrm, ®_addr, &offset_addr); |
1734 | + s->addseg = val; | |
1653 | 1735 | gen_op_mov_reg_A0[ot - OT_WORD][reg](); |
1654 | 1736 | break; |
1655 | 1737 | |
... | ... | @@ -1711,6 +1793,35 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
1711 | 1793 | gen_op_st_T0_A0[ot](); |
1712 | 1794 | gen_op_mov_reg_T1[ot][reg](); |
1713 | 1795 | break; |
1796 | + case 0xc4: /* les Gv */ | |
1797 | + op = R_ES; | |
1798 | + goto do_lxx; | |
1799 | + case 0xc5: /* lds Gv */ | |
1800 | + op = R_DS; | |
1801 | + goto do_lxx; | |
1802 | + case 0x1b2: /* lss Gv */ | |
1803 | + op = R_SS; | |
1804 | + goto do_lxx; | |
1805 | + case 0x1b4: /* lfs Gv */ | |
1806 | + op = R_FS; | |
1807 | + goto do_lxx; | |
1808 | + case 0x1b5: /* lgs Gv */ | |
1809 | + op = R_GS; | |
1810 | + do_lxx: | |
1811 | + ot = dflag ? OT_LONG : OT_WORD; | |
1812 | + modrm = ldub(s->pc++); | |
1813 | + reg = (modrm >> 3) & 7; | |
1814 | + mod = (modrm >> 6) & 3; | |
1815 | + if (mod == 3) | |
1816 | + goto illegal_op; | |
1817 | + gen_op_ld_T1_A0[ot](); | |
1818 | + op_addl_A0_im(1 << (ot - OT_WORD + 1)); | |
1819 | + /* load the segment first to handle exceptions properly */ | |
1820 | + gen_op_lduw_T0_A0(); | |
1821 | + gen_movl_seg_T0(s, op); | |
1822 | + /* then put the data */ | |
1823 | + gen_op_mov_reg_T1[ot][reg](); | |
1824 | + break; | |
1714 | 1825 | |
1715 | 1826 | /************************/ |
1716 | 1827 | /* shifts */ |
... | ... | @@ -2327,12 +2438,12 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
2327 | 2438 | gen_op_popl_T0(); |
2328 | 2439 | gen_op_addl_ESP_im(val); |
2329 | 2440 | gen_op_jmp_T0(); |
2330 | - *is_jmp_ptr = 1; | |
2441 | + s->is_jmp = 1; | |
2331 | 2442 | break; |
2332 | 2443 | case 0xc3: /* ret */ |
2333 | 2444 | gen_op_popl_T0(); |
2334 | 2445 | gen_op_jmp_T0(); |
2335 | - *is_jmp_ptr = 1; | |
2446 | + s->is_jmp = 1; | |
2336 | 2447 | break; |
2337 | 2448 | case 0xe8: /* call */ |
2338 | 2449 | val = insn_get(s, OT_LONG); |
... | ... | @@ -2340,19 +2451,19 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
2340 | 2451 | gen_op_movl_T1_im((long)s->pc); |
2341 | 2452 | gen_op_pushl_T1(); |
2342 | 2453 | gen_op_jmp_im(val); |
2343 | - *is_jmp_ptr = 1; | |
2454 | + s->is_jmp = 1; | |
2344 | 2455 | break; |
2345 | 2456 | case 0xe9: /* jmp */ |
2346 | 2457 | val = insn_get(s, OT_LONG); |
2347 | 2458 | val += (long)s->pc; |
2348 | 2459 | gen_op_jmp_im(val); |
2349 | - *is_jmp_ptr = 1; | |
2460 | + s->is_jmp = 1; | |
2350 | 2461 | break; |
2351 | 2462 | case 0xeb: /* jmp Jb */ |
2352 | 2463 | val = (int8_t)insn_get(s, OT_BYTE); |
2353 | 2464 | val += (long)s->pc; |
2354 | 2465 | gen_op_jmp_im(val); |
2355 | - *is_jmp_ptr = 1; | |
2466 | + s->is_jmp = 1; | |
2356 | 2467 | break; |
2357 | 2468 | case 0x70 ... 0x7f: /* jcc Jb */ |
2358 | 2469 | val = (int8_t)insn_get(s, OT_BYTE); |
... | ... | @@ -2367,7 +2478,7 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
2367 | 2478 | val += (long)s->pc; /* XXX: fix 16 bit wrap */ |
2368 | 2479 | do_jcc: |
2369 | 2480 | gen_jcc(s, b, val); |
2370 | - *is_jmp_ptr = 1; | |
2481 | + s->is_jmp = 1; | |
2371 | 2482 | break; |
2372 | 2483 | |
2373 | 2484 | case 0x190 ... 0x19f: |
... | ... | @@ -2548,19 +2659,19 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
2548 | 2659 | break; |
2549 | 2660 | case 0xcc: /* int3 */ |
2550 | 2661 | gen_op_int3((long)pc_start); |
2551 | - *is_jmp_ptr = 1; | |
2662 | + s->is_jmp = 1; | |
2552 | 2663 | break; |
2553 | 2664 | case 0xcd: /* int N */ |
2554 | 2665 | val = ldub(s->pc++); |
2555 | 2666 | /* XXX: currently we ignore the interrupt number */ |
2556 | 2667 | gen_op_int_im((long)pc_start); |
2557 | - *is_jmp_ptr = 1; | |
2668 | + s->is_jmp = 1; | |
2558 | 2669 | break; |
2559 | 2670 | case 0xce: /* into */ |
2560 | 2671 | if (s->cc_op != CC_OP_DYNAMIC) |
2561 | 2672 | gen_op_set_cc_op(s->cc_op); |
2562 | 2673 | gen_op_into((long)pc_start, (long)s->pc); |
2563 | - *is_jmp_ptr = 1; | |
2674 | + s->is_jmp = 1; | |
2564 | 2675 | break; |
2565 | 2676 | case 0x1c8 ... 0x1cf: /* bswap reg */ |
2566 | 2677 | reg = b & 7; |
... | ... | @@ -2586,38 +2697,43 @@ long disas_insn(DisasContext *s, uint8_t *pc_start, int *is_jmp_ptr) |
2586 | 2697 | return -1; |
2587 | 2698 | } |
2588 | 2699 | return (long)s->pc; |
2700 | + illegal_op: | |
2701 | + error("illegal opcode pc=0x%08Lx", (long)pc_start); | |
2702 | + return -1; | |
2589 | 2703 | } |
2590 | 2704 | |
2591 | 2705 | /* return the next pc */ |
2592 | 2706 | int cpu_x86_gen_code(uint8_t *gen_code_buf, int max_code_size, |
2593 | - int *gen_code_size_ptr, uint8_t *pc_start) | |
2707 | + int *gen_code_size_ptr, uint8_t *pc_start, | |
2708 | + int flags) | |
2594 | 2709 | { |
2595 | 2710 | DisasContext dc1, *dc = &dc1; |
2596 | 2711 | uint8_t *gen_code_end, *pc_ptr; |
2597 | - int is_jmp; | |
2598 | 2712 | long ret; |
2599 | 2713 | #ifdef DEBUG_DISAS |
2600 | 2714 | struct disassemble_info disasm_info; |
2601 | 2715 | #endif |
2602 | - | |
2716 | + dc->code32 = (flags >> GEN_FLAG_CODE32_SHIFT) & 1; | |
2717 | + dc->addseg = (flags >> GEN_FLAG_ADDSEG_SHIFT) & 1; | |
2718 | + dc->f_st = (flags >> GEN_FLAG_ST_SHIFT) & 7; | |
2603 | 2719 | dc->cc_op = CC_OP_DYNAMIC; |
2604 | 2720 | gen_code_ptr = gen_code_buf; |
2605 | 2721 | gen_code_end = gen_code_buf + max_code_size - 4096; |
2606 | 2722 | gen_start(); |
2607 | 2723 | |
2608 | - is_jmp = 0; | |
2724 | + dc->is_jmp = 0; | |
2609 | 2725 | pc_ptr = pc_start; |
2610 | 2726 | do { |
2611 | - ret = disas_insn(dc, pc_ptr, &is_jmp); | |
2727 | + ret = disas_insn(dc, pc_ptr); | |
2612 | 2728 | if (ret == -1) |
2613 | 2729 | error("unknown instruction at PC=0x%x B=%02x %02x", |
2614 | 2730 | pc_ptr, pc_ptr[0], pc_ptr[1]); |
2615 | 2731 | pc_ptr = (void *)ret; |
2616 | - } while (!is_jmp && gen_code_ptr < gen_code_end); | |
2732 | + } while (!dc->is_jmp && gen_code_ptr < gen_code_end); | |
2617 | 2733 | /* we must store the eflags state if it is not already done */ |
2618 | 2734 | if (dc->cc_op != CC_OP_DYNAMIC) |
2619 | 2735 | gen_op_set_cc_op(dc->cc_op); |
2620 | - if (!is_jmp) { | |
2736 | + if (dc->is_jmp != 1) { | |
2621 | 2737 | /* we add an additionnal jmp to update the simulated PC */ |
2622 | 2738 | gen_op_jmp_im(ret); |
2623 | 2739 | } | ... | ... |