Commit 717fc2ad8dbedcb19ec939b752489d2d1a21647b

Authored by bellard
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, &reg_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",
... ...