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 | } | ... | ... |