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", | ... | ... |