Commit 717fc2ad8dbedcb19ec939b752489d2d1a21647b
1 parent
c05bab77
more ring 0 instructions - full x86 MMU emulation based on mmap() syscall - fixed popl (%esp)
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@258 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
249 additions
and
30 deletions
translate-i386.c
... | ... | @@ -24,11 +24,14 @@ |
24 | 24 | #include <inttypes.h> |
25 | 25 | #include <signal.h> |
26 | 26 | #include <assert.h> |
27 | +#include <sys/mman.h> | |
27 | 28 | |
28 | 29 | #include "cpu-i386.h" |
29 | 30 | #include "exec.h" |
30 | 31 | #include "disas.h" |
31 | 32 | |
33 | +//#define DEBUG_MMU | |
34 | + | |
32 | 35 | /* XXX: move that elsewhere */ |
33 | 36 | static uint16_t *gen_opc_ptr; |
34 | 37 | static uint32_t *gen_opparam_ptr; |
... | ... | @@ -59,6 +62,7 @@ typedef struct DisasContext { |
59 | 62 | int iopl; |
60 | 63 | int tf; /* TF cpu flag */ |
61 | 64 | struct TranslationBlock *tb; |
65 | + int popl_esp_hack; /* for correct popl with esp base handling */ | |
62 | 66 | } DisasContext; |
63 | 67 | |
64 | 68 | /* i386 arith/logic operations */ |
... | ... | @@ -862,12 +866,16 @@ static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ |
862 | 866 | } |
863 | 867 | |
864 | 868 | if (base >= 0) { |
869 | + /* for correct popl handling with esp */ | |
870 | + if (base == 4 && s->popl_esp_hack) | |
871 | + disp += 4; | |
865 | 872 | gen_op_movl_A0_reg[base](); |
866 | 873 | if (disp != 0) |
867 | 874 | gen_op_addl_A0_im(disp); |
868 | 875 | } else { |
869 | 876 | gen_op_movl_A0_im(disp); |
870 | 877 | } |
878 | + /* XXX: index == 4 is always invalid */ | |
871 | 879 | if (havesib && (index != 4 || scale != 0)) { |
872 | 880 | gen_op_addl_A0_reg_sN[scale][index](); |
873 | 881 | } |
... | ... | @@ -1894,7 +1902,9 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
1894 | 1902 | ot = dflag ? OT_LONG : OT_WORD; |
1895 | 1903 | modrm = ldub(s->pc++); |
1896 | 1904 | gen_pop_T0(s); |
1905 | + s->popl_esp_hack = 1; | |
1897 | 1906 | gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); |
1907 | + s->popl_esp_hack = 0; | |
1898 | 1908 | gen_pop_update(s); |
1899 | 1909 | break; |
1900 | 1910 | case 0xc8: /* enter */ |
... | ... | @@ -2940,29 +2950,10 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
2940 | 2950 | if (s->vm86 && s->iopl != 3) { |
2941 | 2951 | gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); |
2942 | 2952 | } else { |
2943 | - /* XXX: not restartable */ | |
2944 | - gen_stack_A0(s); | |
2945 | - /* pop offset */ | |
2946 | - gen_op_ld_T0_A0[1 + s->dflag](); | |
2947 | - if (s->dflag == 0) | |
2948 | - gen_op_andl_T0_ffff(); | |
2949 | - /* NOTE: keeping EIP updated is not a problem in case of | |
2950 | - exception */ | |
2951 | - gen_op_jmp_T0(); | |
2952 | - /* pop selector */ | |
2953 | - gen_op_addl_A0_im(2 << s->dflag); | |
2954 | - gen_op_ld_T0_A0[1 + s->dflag](); | |
2955 | - /* pop eflags */ | |
2956 | - gen_op_addl_A0_im(2 << s->dflag); | |
2957 | - gen_op_ld_T1_A0[1 + s->dflag](); | |
2958 | - gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); | |
2959 | - gen_op_movl_T0_T1(); | |
2960 | - if (s->dflag) { | |
2961 | - gen_op_movl_eflags_T0(); | |
2962 | - } else { | |
2963 | - gen_op_movw_eflags_T0(); | |
2964 | - } | |
2965 | - gen_stack_update(s, (6 << s->dflag)); | |
2953 | + if (s->cc_op != CC_OP_DYNAMIC) | |
2954 | + gen_op_set_cc_op(s->cc_op); | |
2955 | + gen_op_jmp_im(pc_start - s->cs_base); | |
2956 | + gen_op_iret_protected(s->dflag); | |
2966 | 2957 | s->cc_op = CC_OP_EFLAGS; |
2967 | 2958 | } |
2968 | 2959 | s->is_jmp = 1; |
... | ... | @@ -3096,10 +3087,18 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
3096 | 3087 | gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); |
3097 | 3088 | } else { |
3098 | 3089 | gen_pop_T0(s); |
3099 | - if (s->dflag) { | |
3100 | - gen_op_movl_eflags_T0(); | |
3090 | + if (s->cpl == 0) { | |
3091 | + if (s->dflag) { | |
3092 | + gen_op_movl_eflags_T0_cpl0(); | |
3093 | + } else { | |
3094 | + gen_op_movw_eflags_T0_cpl0(); | |
3095 | + } | |
3101 | 3096 | } else { |
3102 | - gen_op_movw_eflags_T0(); | |
3097 | + if (s->dflag) { | |
3098 | + gen_op_movl_eflags_T0(); | |
3099 | + } else { | |
3100 | + gen_op_movw_eflags_T0(); | |
3101 | + } | |
3103 | 3102 | } |
3104 | 3103 | gen_pop_update(s); |
3105 | 3104 | s->cc_op = CC_OP_EFLAGS; |
... | ... | @@ -3358,8 +3357,15 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
3358 | 3357 | gen_op_cpuid(); |
3359 | 3358 | break; |
3360 | 3359 | case 0xf4: /* hlt */ |
3361 | - /* XXX: if cpl == 0, then should do something else */ | |
3362 | - gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); | |
3360 | + if (s->cpl != 0) { | |
3361 | + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); | |
3362 | + } else { | |
3363 | + if (s->cc_op != CC_OP_DYNAMIC) | |
3364 | + gen_op_set_cc_op(s->cc_op); | |
3365 | + gen_op_jmp_im(s->pc - s->cs_base); | |
3366 | + gen_op_hlt(); | |
3367 | + s->is_jmp = 1; | |
3368 | + } | |
3363 | 3369 | break; |
3364 | 3370 | case 0x100: |
3365 | 3371 | modrm = ldub(s->pc++); |
... | ... | @@ -3462,6 +3468,16 @@ long disas_insn(DisasContext *s, uint8_t *pc_start) |
3462 | 3468 | gen_op_lmsw_T0(); |
3463 | 3469 | } |
3464 | 3470 | break; |
3471 | + case 7: /* invlpg */ | |
3472 | + if (s->cpl != 0) { | |
3473 | + gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); | |
3474 | + } else { | |
3475 | + if (mod == 3) | |
3476 | + goto illegal_op; | |
3477 | + gen_lea_modrm(s, modrm, ®_addr, &offset_addr); | |
3478 | + gen_op_invlpg_A0(); | |
3479 | + } | |
3480 | + break; | |
3465 | 3481 | default: |
3466 | 3482 | goto illegal_op; |
3467 | 3483 | } |
... | ... | @@ -3866,7 +3882,7 @@ static void optimize_flags(uint16_t *opc_buf, int opc_buf_len) |
3866 | 3882 | /* generate intermediate code in gen_opc_buf and gen_opparam_buf for |
3867 | 3883 | basic block 'tb'. If search_pc is TRUE, also generate PC |
3868 | 3884 | information for each intermediate instruction. */ |
3869 | -int gen_intermediate_code(TranslationBlock *tb, int search_pc) | |
3885 | +static inline int gen_intermediate_code_internal(TranslationBlock *tb, int search_pc) | |
3870 | 3886 | { |
3871 | 3887 | DisasContext dc1, *dc = &dc1; |
3872 | 3888 | uint8_t *pc_ptr; |
... | ... | @@ -3892,7 +3908,8 @@ int gen_intermediate_code(TranslationBlock *tb, int search_pc) |
3892 | 3908 | dc->cc_op = CC_OP_DYNAMIC; |
3893 | 3909 | dc->cs_base = cs_base; |
3894 | 3910 | dc->tb = tb; |
3895 | - | |
3911 | + dc->popl_esp_hack = 0; | |
3912 | + | |
3896 | 3913 | gen_opc_ptr = gen_opc_buf; |
3897 | 3914 | gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; |
3898 | 3915 | gen_opparam_ptr = gen_opparam_buf; |
... | ... | @@ -3908,6 +3925,7 @@ int gen_intermediate_code(TranslationBlock *tb, int search_pc) |
3908 | 3925 | while (lj < j) |
3909 | 3926 | gen_opc_instr_start[lj++] = 0; |
3910 | 3927 | gen_opc_pc[lj] = (uint32_t)pc_ptr; |
3928 | + gen_opc_cc_op[lj] = dc->cc_op; | |
3911 | 3929 | gen_opc_instr_start[lj] = 1; |
3912 | 3930 | } |
3913 | 3931 | } |
... | ... | @@ -3974,6 +3992,16 @@ int gen_intermediate_code(TranslationBlock *tb, int search_pc) |
3974 | 3992 | return 0; |
3975 | 3993 | } |
3976 | 3994 | |
3995 | +int gen_intermediate_code(TranslationBlock *tb) | |
3996 | +{ | |
3997 | + return gen_intermediate_code_internal(tb, 0); | |
3998 | +} | |
3999 | + | |
4000 | +int gen_intermediate_code_pc(TranslationBlock *tb) | |
4001 | +{ | |
4002 | + return gen_intermediate_code_internal(tb, 1); | |
4003 | +} | |
4004 | + | |
3977 | 4005 | CPUX86State *cpu_x86_init(void) |
3978 | 4006 | { |
3979 | 4007 | CPUX86State *env; |
... | ... | @@ -4006,6 +4034,197 @@ void cpu_x86_close(CPUX86State *env) |
4006 | 4034 | free(env); |
4007 | 4035 | } |
4008 | 4036 | |
4037 | +/***********************************************************/ | |
4038 | +/* x86 mmu */ | |
4039 | + | |
4040 | +/* called when cr3 or PG bit are modified */ | |
4041 | +static int last_pg_state = -1; | |
4042 | +int phys_ram_size; | |
4043 | +int phys_ram_fd; | |
4044 | +uint8_t *phys_ram_base; | |
4045 | + | |
4046 | +void cpu_x86_update_cr0(CPUX86State *env) | |
4047 | +{ | |
4048 | + int pg_state; | |
4049 | + void *map_addr; | |
4050 | + | |
4051 | +#ifdef DEBUG_MMU | |
4052 | + printf("CR0 update: CR0=0x%08x\n", env->cr[0]); | |
4053 | +#endif | |
4054 | + pg_state = env->cr[0] & CR0_PG_MASK; | |
4055 | + if (pg_state != last_pg_state) { | |
4056 | + if (!pg_state) { | |
4057 | + /* we map the physical memory at address 0 */ | |
4058 | + | |
4059 | + map_addr = mmap((void *)0, phys_ram_size, PROT_WRITE | PROT_READ, | |
4060 | + MAP_SHARED | MAP_FIXED, phys_ram_fd, 0); | |
4061 | + if (map_addr == MAP_FAILED) { | |
4062 | + fprintf(stderr, | |
4063 | + "Could not map physical memory at host address 0x%08x\n", | |
4064 | + 0); | |
4065 | + exit(1); | |
4066 | + } | |
4067 | + page_set_flags(0, phys_ram_size, | |
4068 | + PAGE_VALID | PAGE_READ | PAGE_WRITE | PAGE_EXEC); | |
4069 | + } else { | |
4070 | + /* we unmap the physical memory */ | |
4071 | + munmap((void *)0, phys_ram_size); | |
4072 | + page_set_flags(0, phys_ram_size, 0); | |
4073 | + } | |
4074 | + last_pg_state = pg_state; | |
4075 | + } | |
4076 | +} | |
4077 | + | |
4078 | +void cpu_x86_update_cr3(CPUX86State *env) | |
4079 | +{ | |
4080 | + if (env->cr[0] & CR0_PG_MASK) { | |
4081 | +#ifdef DEBUG_MMU | |
4082 | + printf("CR3 update: CR3=%08x\n", env->cr[3]); | |
4083 | +#endif | |
4084 | + page_unmap(); | |
4085 | + } | |
4086 | +} | |
4087 | + | |
4088 | +void cpu_x86_init_mmu(CPUX86State *env) | |
4089 | +{ | |
4090 | + last_pg_state = -1; | |
4091 | + cpu_x86_update_cr0(env); | |
4092 | +} | |
4093 | + | |
4094 | +void cpu_x86_flush_tlb(CPUX86State *env, uint32_t addr) | |
4095 | +{ | |
4096 | +} | |
4097 | + | |
4098 | +/* return value: | |
4099 | + -1 = cannot handle fault | |
4100 | + 0 = nothing more to do | |
4101 | + 1 = generate PF fault | |
4102 | +*/ | |
4103 | +int cpu_x86_handle_mmu_fault(CPUX86State *env, uint32_t addr, int is_write) | |
4104 | +{ | |
4105 | + uint8_t *pde_ptr, *pte_ptr; | |
4106 | + uint32_t pde, pte, virt_addr; | |
4107 | + int cpl, error_code, is_dirty, is_user, prot, page_size; | |
4108 | + void *map_addr; | |
4109 | + | |
4110 | + cpl = env->segs[R_CS].selector & 3; | |
4111 | + is_user = (cpl == 3); | |
4112 | + | |
4113 | +#ifdef DEBUG_MMU | |
4114 | + printf("MMU fault: addr=0x%08x w=%d u=%d eip=%08x\n", | |
4115 | + addr, is_write, is_user, env->eip); | |
4116 | +#endif | |
4117 | + | |
4118 | + if (env->user_mode_only) { | |
4119 | + /* user mode only emulation */ | |
4120 | + error_code = 0; | |
4121 | + goto do_fault; | |
4122 | + } | |
4123 | + | |
4124 | + if (!(env->cr[0] & CR0_PG_MASK)) | |
4125 | + return -1; | |
4126 | + | |
4127 | + /* page directory entry */ | |
4128 | + pde_ptr = phys_ram_base + ((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3)); | |
4129 | + pde = ldl(pde_ptr); | |
4130 | + if (!(pde & PG_PRESENT_MASK)) { | |
4131 | + error_code = 0; | |
4132 | + goto do_fault; | |
4133 | + } | |
4134 | + if (is_user) { | |
4135 | + if (!(pde & PG_USER_MASK)) | |
4136 | + goto do_fault_protect; | |
4137 | + if (is_write && !(pde & PG_RW_MASK)) | |
4138 | + goto do_fault_protect; | |
4139 | + } else { | |
4140 | + if ((env->cr[0] & CR0_WP_MASK) && (pde & PG_USER_MASK) && | |
4141 | + is_write && !(pde & PG_RW_MASK)) | |
4142 | + goto do_fault_protect; | |
4143 | + } | |
4144 | + /* if PSE bit is set, then we use a 4MB page */ | |
4145 | + if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { | |
4146 | + is_dirty = is_write && !(pde & PG_DIRTY_MASK); | |
4147 | + if (!(pde & PG_ACCESSED_MASK)) { | |
4148 | + pde |= PG_ACCESSED_MASK; | |
4149 | + if (is_dirty) | |
4150 | + pde |= PG_DIRTY_MASK; | |
4151 | + stl(pde_ptr, pde); | |
4152 | + } | |
4153 | + | |
4154 | + pte = pde & ~0x003ff000; /* align to 4MB */ | |
4155 | + page_size = 4096 * 1024; | |
4156 | + virt_addr = addr & ~0x003fffff; | |
4157 | + } else { | |
4158 | + if (!(pde & PG_ACCESSED_MASK)) { | |
4159 | + pde |= PG_ACCESSED_MASK; | |
4160 | + stl(pde_ptr, pde); | |
4161 | + } | |
4162 | + | |
4163 | + /* page directory entry */ | |
4164 | + pte_ptr = phys_ram_base + ((pde & ~0xfff) + ((addr >> 10) & 0xffc)); | |
4165 | + pte = ldl(pte_ptr); | |
4166 | + if (!(pte & PG_PRESENT_MASK)) { | |
4167 | + error_code = 0; | |
4168 | + goto do_fault; | |
4169 | + } | |
4170 | + if (is_user) { | |
4171 | + if (!(pte & PG_USER_MASK)) | |
4172 | + goto do_fault_protect; | |
4173 | + if (is_write && !(pte & PG_RW_MASK)) | |
4174 | + goto do_fault_protect; | |
4175 | + } else { | |
4176 | + if ((env->cr[0] & CR0_WP_MASK) && (pte & PG_USER_MASK) && | |
4177 | + is_write && !(pte & PG_RW_MASK)) | |
4178 | + goto do_fault_protect; | |
4179 | + } | |
4180 | + is_dirty = is_write && !(pte & PG_DIRTY_MASK); | |
4181 | + if (!(pte & PG_ACCESSED_MASK) || is_dirty) { | |
4182 | + pte |= PG_ACCESSED_MASK; | |
4183 | + if (is_dirty) | |
4184 | + pte |= PG_DIRTY_MASK; | |
4185 | + stl(pte_ptr, pte); | |
4186 | + } | |
4187 | + page_size = 4096; | |
4188 | + virt_addr = addr & ~0xfff; | |
4189 | + } | |
4190 | + /* the page can be put in the TLB */ | |
4191 | + prot = PROT_READ; | |
4192 | + if (is_user) { | |
4193 | + if (pte & PG_RW_MASK) | |
4194 | + prot |= PROT_WRITE; | |
4195 | + } else { | |
4196 | + if (!(env->cr[0] & CR0_WP_MASK) || !(pte & PG_USER_MASK) || | |
4197 | + (pte & PG_RW_MASK)) | |
4198 | + prot |= PROT_WRITE; | |
4199 | + } | |
4200 | + map_addr = mmap((void *)virt_addr, page_size, prot, | |
4201 | + MAP_SHARED | MAP_FIXED, phys_ram_fd, pte & ~0xfff); | |
4202 | + if (map_addr == MAP_FAILED) { | |
4203 | + fprintf(stderr, | |
4204 | + "mmap failed when mapped physical address 0x%08x to virtual address 0x%08x\n", | |
4205 | + pte & ~0xfff, virt_addr); | |
4206 | + exit(1); | |
4207 | + } | |
4208 | + page_set_flags(virt_addr, virt_addr + page_size, | |
4209 | + PAGE_VALID | PAGE_EXEC | prot); | |
4210 | +#ifdef DEBUG_MMU | |
4211 | + printf("mmaping 0x%08x to virt 0x%08x pse=%d\n", | |
4212 | + pte & ~0xfff, virt_addr, (page_size != 4096)); | |
4213 | +#endif | |
4214 | + return 0; | |
4215 | + do_fault_protect: | |
4216 | + error_code = PG_ERROR_P_MASK; | |
4217 | + do_fault: | |
4218 | + env->cr[2] = addr; | |
4219 | + env->error_code = (is_write << PG_ERROR_W_BIT) | error_code; | |
4220 | + if (is_user) | |
4221 | + env->error_code |= PG_ERROR_U_MASK; | |
4222 | + return 1; | |
4223 | +} | |
4224 | + | |
4225 | +/***********************************************************/ | |
4226 | +/* x86 debug */ | |
4227 | + | |
4009 | 4228 | static const char *cc_op_str[] = { |
4010 | 4229 | "DYNAMIC", |
4011 | 4230 | "EFLAGS", | ... | ... |