24 |
#include <inttypes.h> |
#include <inttypes.h> |
25 |
#include <signal.h> |
#include <signal.h> |
26 |
#include <assert.h> |
#include <assert.h> |
27 |
|
#include <sys/mman.h> |
28 |
|
|
29 |
#include "cpu-i386.h" |
#include "cpu-i386.h" |
30 |
#include "exec.h" |
#include "exec.h" |
31 |
#include "disas.h" |
#include "disas.h" |
32 |
|
|
33 |
|
//#define DEBUG_MMU |
34 |
|
|
35 |
/* XXX: move that elsewhere */ |
/* XXX: move that elsewhere */ |
36 |
static uint16_t *gen_opc_ptr; |
static uint16_t *gen_opc_ptr; |
37 |
static uint32_t *gen_opparam_ptr; |
static uint32_t *gen_opparam_ptr; |
62 |
int iopl; |
int iopl; |
63 |
int tf; /* TF cpu flag */ |
int tf; /* TF cpu flag */ |
64 |
struct TranslationBlock *tb; |
struct TranslationBlock *tb; |
65 |
|
int popl_esp_hack; /* for correct popl with esp base handling */ |
66 |
} DisasContext; |
} DisasContext; |
67 |
|
|
68 |
/* i386 arith/logic operations */ |
/* i386 arith/logic operations */ |
866 |
} |
} |
867 |
|
|
868 |
if (base >= 0) { |
if (base >= 0) { |
869 |
|
/* for correct popl handling with esp */ |
870 |
|
if (base == 4 && s->popl_esp_hack) |
871 |
|
disp += 4; |
872 |
gen_op_movl_A0_reg[base](); |
gen_op_movl_A0_reg[base](); |
873 |
if (disp != 0) |
if (disp != 0) |
874 |
gen_op_addl_A0_im(disp); |
gen_op_addl_A0_im(disp); |
875 |
} else { |
} else { |
876 |
gen_op_movl_A0_im(disp); |
gen_op_movl_A0_im(disp); |
877 |
} |
} |
878 |
|
/* XXX: index == 4 is always invalid */ |
879 |
if (havesib && (index != 4 || scale != 0)) { |
if (havesib && (index != 4 || scale != 0)) { |
880 |
gen_op_addl_A0_reg_sN[scale][index](); |
gen_op_addl_A0_reg_sN[scale][index](); |
881 |
} |
} |
1902 |
ot = dflag ? OT_LONG : OT_WORD; |
ot = dflag ? OT_LONG : OT_WORD; |
1903 |
modrm = ldub(s->pc++); |
modrm = ldub(s->pc++); |
1904 |
gen_pop_T0(s); |
gen_pop_T0(s); |
1905 |
|
s->popl_esp_hack = 1; |
1906 |
gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); |
gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); |
1907 |
|
s->popl_esp_hack = 0; |
1908 |
gen_pop_update(s); |
gen_pop_update(s); |
1909 |
break; |
break; |
1910 |
case 0xc8: /* enter */ |
case 0xc8: /* enter */ |
2950 |
if (s->vm86 && s->iopl != 3) { |
if (s->vm86 && s->iopl != 3) { |
2951 |
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); |
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); |
2952 |
} else { |
} else { |
2953 |
/* XXX: not restartable */ |
if (s->cc_op != CC_OP_DYNAMIC) |
2954 |
gen_stack_A0(s); |
gen_op_set_cc_op(s->cc_op); |
2955 |
/* pop offset */ |
gen_op_jmp_im(pc_start - s->cs_base); |
2956 |
gen_op_ld_T0_A0[1 + s->dflag](); |
gen_op_iret_protected(s->dflag); |
|
if (s->dflag == 0) |
|
|
gen_op_andl_T0_ffff(); |
|
|
/* NOTE: keeping EIP updated is not a problem in case of |
|
|
exception */ |
|
|
gen_op_jmp_T0(); |
|
|
/* pop selector */ |
|
|
gen_op_addl_A0_im(2 << s->dflag); |
|
|
gen_op_ld_T0_A0[1 + s->dflag](); |
|
|
/* pop eflags */ |
|
|
gen_op_addl_A0_im(2 << s->dflag); |
|
|
gen_op_ld_T1_A0[1 + s->dflag](); |
|
|
gen_movl_seg_T0(s, R_CS, pc_start - s->cs_base); |
|
|
gen_op_movl_T0_T1(); |
|
|
if (s->dflag) { |
|
|
gen_op_movl_eflags_T0(); |
|
|
} else { |
|
|
gen_op_movw_eflags_T0(); |
|
|
} |
|
|
gen_stack_update(s, (6 << s->dflag)); |
|
2957 |
s->cc_op = CC_OP_EFLAGS; |
s->cc_op = CC_OP_EFLAGS; |
2958 |
} |
} |
2959 |
s->is_jmp = 1; |
s->is_jmp = 1; |
3087 |
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); |
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); |
3088 |
} else { |
} else { |
3089 |
gen_pop_T0(s); |
gen_pop_T0(s); |
3090 |
if (s->dflag) { |
if (s->cpl == 0) { |
3091 |
gen_op_movl_eflags_T0(); |
if (s->dflag) { |
3092 |
|
gen_op_movl_eflags_T0_cpl0(); |
3093 |
|
} else { |
3094 |
|
gen_op_movw_eflags_T0_cpl0(); |
3095 |
|
} |
3096 |
} else { |
} else { |
3097 |
gen_op_movw_eflags_T0(); |
if (s->dflag) { |
3098 |
|
gen_op_movl_eflags_T0(); |
3099 |
|
} else { |
3100 |
|
gen_op_movw_eflags_T0(); |
3101 |
|
} |
3102 |
} |
} |
3103 |
gen_pop_update(s); |
gen_pop_update(s); |
3104 |
s->cc_op = CC_OP_EFLAGS; |
s->cc_op = CC_OP_EFLAGS; |
3357 |
gen_op_cpuid(); |
gen_op_cpuid(); |
3358 |
break; |
break; |
3359 |
case 0xf4: /* hlt */ |
case 0xf4: /* hlt */ |
3360 |
/* XXX: if cpl == 0, then should do something else */ |
if (s->cpl != 0) { |
3361 |
gen_exception(s, EXCP0D_GPF, pc_start - s->cs_base); |
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 |
|
} |
3369 |
break; |
break; |
3370 |
case 0x100: |
case 0x100: |
3371 |
modrm = ldub(s->pc++); |
modrm = ldub(s->pc++); |
3468 |
gen_op_lmsw_T0(); |
gen_op_lmsw_T0(); |
3469 |
} |
} |
3470 |
break; |
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; |
3481 |
default: |
default: |
3482 |
goto illegal_op; |
goto illegal_op; |
3483 |
} |
} |
3882 |
/* generate intermediate code in gen_opc_buf and gen_opparam_buf for |
/* generate intermediate code in gen_opc_buf and gen_opparam_buf for |
3883 |
basic block 'tb'. If search_pc is TRUE, also generate PC |
basic block 'tb'. If search_pc is TRUE, also generate PC |
3884 |
information for each intermediate instruction. */ |
information for each intermediate instruction. */ |
3885 |
int gen_intermediate_code(TranslationBlock *tb, int search_pc) |
static inline int gen_intermediate_code_internal(TranslationBlock *tb, int search_pc) |
3886 |
{ |
{ |
3887 |
DisasContext dc1, *dc = &dc1; |
DisasContext dc1, *dc = &dc1; |
3888 |
uint8_t *pc_ptr; |
uint8_t *pc_ptr; |
3908 |
dc->cc_op = CC_OP_DYNAMIC; |
dc->cc_op = CC_OP_DYNAMIC; |
3909 |
dc->cs_base = cs_base; |
dc->cs_base = cs_base; |
3910 |
dc->tb = tb; |
dc->tb = tb; |
3911 |
|
dc->popl_esp_hack = 0; |
3912 |
|
|
3913 |
gen_opc_ptr = gen_opc_buf; |
gen_opc_ptr = gen_opc_buf; |
3914 |
gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; |
gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; |
3915 |
gen_opparam_ptr = gen_opparam_buf; |
gen_opparam_ptr = gen_opparam_buf; |
3925 |
while (lj < j) |
while (lj < j) |
3926 |
gen_opc_instr_start[lj++] = 0; |
gen_opc_instr_start[lj++] = 0; |
3927 |
gen_opc_pc[lj] = (uint32_t)pc_ptr; |
gen_opc_pc[lj] = (uint32_t)pc_ptr; |
3928 |
|
gen_opc_cc_op[lj] = dc->cc_op; |
3929 |
gen_opc_instr_start[lj] = 1; |
gen_opc_instr_start[lj] = 1; |
3930 |
} |
} |
3931 |
} |
} |
3992 |
return 0; |
return 0; |
3993 |
} |
} |
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 |
|
|
4005 |
CPUX86State *cpu_x86_init(void) |
CPUX86State *cpu_x86_init(void) |
4006 |
{ |
{ |
4007 |
CPUX86State *env; |
CPUX86State *env; |
4034 |
free(env); |
free(env); |
4035 |
} |
} |
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 |
|
|
4228 |
static const char *cc_op_str[] = { |
static const char *cc_op_str[] = { |
4229 |
"DYNAMIC", |
"DYNAMIC", |
4230 |
"EFLAGS", |
"EFLAGS", |