Commit 2e255c6b authored by bellard's avatar bellard
Browse files

faster and more accurate segment handling


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@373 c046a42c-6fe2-441c-8c8c-71466251a162
parent 3f337316
......@@ -182,7 +182,7 @@ int cpu_exec(CPUState *env1)
tmp_T0 = T0;
#endif
interrupt_request = env->interrupt_request;
if (interrupt_request) {
if (__builtin_expect(interrupt_request, 0)) {
#if defined(TARGET_I386)
/* if hardware interrupt pending, we execute it */
if ((interrupt_request & CPU_INTERRUPT_HARD) &&
......@@ -238,15 +238,7 @@ int cpu_exec(CPUState *env1)
always be the same before a given translated block
is executed. */
#if defined(TARGET_I386)
flags = (env->segs[R_CS].flags & DESC_B_MASK)
>> (DESC_B_SHIFT - HF_CS32_SHIFT);
flags |= (env->segs[R_SS].flags & DESC_B_MASK)
>> (DESC_B_SHIFT - HF_SS32_SHIFT);
flags |= (((unsigned long)env->segs[R_DS].base |
(unsigned long)env->segs[R_ES].base |
(unsigned long)env->segs[R_SS].base) != 0) <<
HF_ADDSEG_SHIFT;
flags |= env->hflags;
flags = env->hflags;
flags |= (env->eflags & (IOPL_MASK | TF_MASK | VM_MASK));
cs_base = env->segs[R_CS].base;
pc = cs_base + env->eip;
......@@ -402,13 +394,9 @@ void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector)
saved_env = env;
env = s;
if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
SegmentCache *sc;
selector &= 0xffff;
sc = &env->segs[seg_reg];
sc->base = (void *)(selector << 4);
sc->limit = 0xffff;
sc->flags = 0;
sc->selector = selector;
cpu_x86_load_seg_cache(env, seg_reg, selector,
(uint8_t *)(selector << 4), 0xffff, 0);
} else {
load_seg(seg_reg, selector, 0);
}
......
......@@ -109,7 +109,7 @@
#define HF_SOFTMMU_MASK (1 << HF_SOFTMMU_SHIFT)
#define HF_INHIBIT_IRQ_MASK (1 << HF_INHIBIT_IRQ_SHIFT)
#define HF_CS32_MASK (1 << HF_CS32_SHIFT)
#define HF_SS32_MASK (1 << HF_CS32_SHIFT)
#define HF_SS32_MASK (1 << HF_SS32_SHIFT)
#define HF_ADDSEG_MASK (1 << HF_ADDSEG_SHIFT)
#define CR0_PE_MASK (1 << 0)
......@@ -323,8 +323,43 @@ int cpu_x86_exec(CPUX86State *s);
void cpu_x86_close(CPUX86State *s);
int cpu_x86_get_pic_interrupt(CPUX86State *s);
/* needed to load some predefinied segment registers */
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
/* this function must always be used to load data in the segment
cache: it synchronizes the hflags with the segment cache values */
static inline void cpu_x86_load_seg_cache(CPUX86State *env,
int seg_reg, unsigned int selector,
uint8_t *base, unsigned int limit,
unsigned int flags)
{
SegmentCache *sc;
unsigned int new_hflags;
sc = &env->segs[seg_reg];
sc->selector = selector;
sc->base = base;
sc->limit = limit;
sc->flags = flags;
/* update the hidden flags */
new_hflags = (env->segs[R_CS].flags & DESC_B_MASK)
>> (DESC_B_SHIFT - HF_CS32_SHIFT);
new_hflags |= (env->segs[R_SS].flags & DESC_B_MASK)
>> (DESC_B_SHIFT - HF_SS32_SHIFT);
if (!(env->cr[0] & CR0_PE_MASK) || (env->eflags & VM_MASK)) {
/* XXX: try to avoid this test. The problem comes from the
fact that is real mode or vm86 mode we only modify the
'base' and 'selector' fields of the segment cache to go
faster. A solution may be to force addseg to one in
translate-i386.c. */
new_hflags |= HF_ADDSEG_MASK;
} else {
new_hflags |= (((unsigned long)env->segs[R_DS].base |
(unsigned long)env->segs[R_ES].base |
(unsigned long)env->segs[R_SS].base) != 0) <<
HF_ADDSEG_SHIFT;
}
env->hflags = (env->hflags &
~(HF_CS32_MASK | HF_SS32_MASK | HF_ADDSEG_MASK)) | new_hflags;
}
/* wrapper, just in case memory mappings must be changed */
static inline void cpu_x86_set_cpl(CPUX86State *s, int cpl)
......@@ -336,7 +371,9 @@ static inline void cpu_x86_set_cpl(CPUX86State *s, int cpl)
#endif
}
/* simulate fsave/frstor */
/* the following helpers are only usable in user mode simulation as
they can trigger unexpected exceptions */
void cpu_x86_load_seg(CPUX86State *s, int seg_reg, int selector);
void cpu_x86_fsave(CPUX86State *s, uint8_t *ptr, int data32);
void cpu_x86_frstor(CPUX86State *s, uint8_t *ptr, int data32);
......
......@@ -182,6 +182,34 @@ static inline int load_segment(uint32_t *e1_ptr, uint32_t *e2_ptr,
return 0;
}
static inline unsigned int get_seg_limit(uint32_t e1, uint32_t e2)
{
unsigned int limit;
limit = (e1 & 0xffff) | (e2 & 0x000f0000);
if (e2 & DESC_G_MASK)
limit = (limit << 12) | 0xfff;
return limit;
}
static inline uint8_t *get_seg_base(uint32_t e1, uint32_t e2)
{
return (uint8_t *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
}
static inline void load_seg_cache_raw_dt(SegmentCache *sc, uint32_t e1, uint32_t e2)
{
sc->base = get_seg_base(e1, e2);
sc->limit = get_seg_limit(e1, e2);
sc->flags = e2;
}
/* init the segment cache in vm86 mode. */
static inline void load_seg_vm(int seg, int selector)
{
selector &= 0xffff;
cpu_x86_load_seg_cache(env, seg, selector,
(uint8_t *)(selector << 4), 0xffff, 0);
}
/* protected mode interrupt */
static void do_interrupt_protected(int intno, int is_int, int error_code,
......@@ -288,7 +316,11 @@ static void do_interrupt_protected(int intno, int is_int, int error_code,
if (new_stack) {
old_esp = ESP;
old_ss = env->segs[R_SS].selector;
load_seg(R_SS, ss, env->eip);
ss = (ss & ~3) | dpl;
cpu_x86_load_seg_cache(env, R_SS, ss,
get_seg_base(ss_e1, ss_e2),
get_seg_limit(ss_e1, ss_e2),
ss_e2);
} else {
old_esp = 0;
old_ss = 0;
......@@ -299,7 +331,12 @@ static void do_interrupt_protected(int intno, int is_int, int error_code,
else
old_eip = env->eip;
old_cs = env->segs[R_CS].selector;
load_seg(R_CS, selector, env->eip);
selector = (selector & ~3) | dpl;
cpu_x86_load_seg_cache(env, R_CS, selector,
get_seg_base(e1, e2),
get_seg_limit(e1, e2),
e2);
cpu_x86_set_cpl(env, dpl);
env->eip = offset;
ESP = esp - push_size;
ssp = env->segs[R_SS].base + esp;
......@@ -593,15 +630,6 @@ void helper_cpuid(void)
}
}
static inline void load_seg_cache(SegmentCache *sc, uint32_t e1, uint32_t e2)
{
sc->base = (void *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
sc->limit = (e1 & 0xffff) | (e2 & 0x000f0000);
if (e2 & DESC_G_MASK)
sc->limit = (sc->limit << 12) | 0xfff;
sc->flags = e2;
}
void helper_lldt_T0(void)
{
int selector;
......@@ -629,7 +657,7 @@ void helper_lldt_T0(void)
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
load_seg_cache(&env->ldt, e1, e2);
load_seg_cache_raw_dt(&env->ldt, e1, e2);
}
env->ldt.selector = selector;
}
......@@ -664,30 +692,26 @@ void helper_ltr_T0(void)
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
load_seg_cache(&env->tr, e1, e2);
load_seg_cache_raw_dt(&env->tr, e1, e2);
e2 |= 0x00000200; /* set the busy bit */
stl(ptr + 4, e2);
}
env->tr.selector = selector;
}
/* only works if protected mode and not VM86 */
/* only works if protected mode and not VM86. Calling load_seg with
seg_reg == R_CS is discouraged */
void load_seg(int seg_reg, int selector, unsigned int cur_eip)
{
SegmentCache *sc;
uint32_t e1, e2;
sc = &env->segs[seg_reg];
if ((selector & 0xfffc) == 0) {
/* null selector case */
if (seg_reg == R_SS) {
EIP = cur_eip;
raise_exception_err(EXCP0D_GPF, 0);
} else {
/* XXX: each access should trigger an exception */
sc->base = NULL;
sc->limit = 0;
sc->flags = 0;
cpu_x86_load_seg_cache(env, seg_reg, selector, NULL, 0, 0);
}
} else {
if (load_segment(&e1, &e2, selector) != 0) {
......@@ -719,24 +743,22 @@ void load_seg(int seg_reg, int selector, unsigned int cur_eip)
else
raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
}
load_seg_cache(sc, e1, e2);
cpu_x86_load_seg_cache(env, seg_reg, selector,
get_seg_base(e1, e2),
get_seg_limit(e1, e2),
e2);
#if 0
fprintf(logfile, "load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx flags=%08x\n",
selector, (unsigned long)sc->base, sc->limit, sc->flags);
#endif
}
if (seg_reg == R_CS) {
cpu_x86_set_cpl(env, selector & 3);
}
sc->selector = selector;
}
/* protected mode jump */
void helper_ljmp_protected_T0_T1(void)
{
int new_cs, new_eip;
SegmentCache sc1;
uint32_t e1, e2, cpl, dpl, rpl;
uint32_t e1, e2, cpl, dpl, rpl, limit;
new_cs = T0;
new_eip = T1;
......@@ -763,13 +785,11 @@ void helper_ljmp_protected_T0_T1(void)
}
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
load_seg_cache(&sc1, e1, e2);
if (new_eip > sc1.limit)
limit = get_seg_limit(e1, e2);
if (new_eip > limit)
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
env->segs[R_CS].base = sc1.base;
env->segs[R_CS].limit = sc1.limit;
env->segs[R_CS].flags = sc1.flags;
env->segs[R_CS].selector = (new_cs & 0xfffc) | cpl;
cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
get_seg_base(e1, e2), limit, e2);
EIP = new_eip;
} else {
cpu_abort(env, "jmp to call/task gate not supported 0x%04x:0x%08x",
......@@ -816,10 +836,9 @@ void helper_lcall_real_T0_T1(int shift, int next_eip)
void helper_lcall_protected_T0_T1(int shift, int next_eip)
{
int new_cs, new_eip;
SegmentCache sc1;
uint32_t e1, e2, cpl, dpl, rpl, selector, offset, param_count;
uint32_t ss, ss_e1, ss_e2, push_size, sp, type, ss_dpl;
uint32_t old_ss, old_esp, val, i;
uint32_t old_ss, old_esp, val, i, limit;
uint8_t *ssp, *old_ssp;
new_cs = T0;
......@@ -865,18 +884,16 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip)
}
sp -= (4 << shift);
load_seg_cache(&sc1, e1, e2);
if (new_eip > sc1.limit)
limit = get_seg_limit(e1, e2);
if (new_eip > limit)
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
/* from this point, not restartable */
if (!(env->segs[R_SS].flags & DESC_B_MASK))
ESP = (ESP & 0xffff0000) | (sp & 0xffff);
else
ESP = sp;
env->segs[R_CS].base = sc1.base;
env->segs[R_CS].limit = sc1.limit;
env->segs[R_CS].flags = sc1.flags;
env->segs[R_CS].selector = (new_cs & 0xfffc) | cpl;
cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
get_seg_base(e1, e2), limit, e2);
EIP = new_eip;
} else {
/* check gate type */
......@@ -947,7 +964,11 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip)
old_ssp = env->segs[R_SS].base + old_esp;
/* XXX: from this point not restartable */
load_seg(R_SS, ss, env->eip);
ss = (ss & ~3) | dpl;
cpu_x86_load_seg_cache(env, R_SS, ss,
get_seg_base(ss_e1, ss_e2),
get_seg_limit(ss_e1, ss_e2),
ss_e2);
if (!(env->segs[R_SS].flags & DESC_B_MASK))
sp &= 0xffff;
......@@ -994,7 +1015,13 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip)
}
sp -= push_size;
load_seg(R_CS, selector, env->eip);
selector = (selector & ~3) | dpl;
cpu_x86_load_seg_cache(env, R_CS, selector,
get_seg_base(e1, e2),
get_seg_limit(e1, e2),
e2);
cpu_x86_set_cpl(env, dpl);
/* from this point, not restartable if same priviledge */
if (!(env->segs[R_SS].flags & DESC_B_MASK))
ESP = (ESP & 0xffff0000) | (sp & 0xffff);
......@@ -1004,17 +1031,6 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip)
}
}
/* init the segment cache in vm86 mode */
static inline void load_seg_vm(int seg, int selector)
{
SegmentCache *sc = &env->segs[seg];
selector &= 0xffff;
sc->base = (uint8_t *)(selector << 4);
sc->selector = selector;
sc->flags = 0;
sc->limit = 0xffff;
}
/* real mode iret */
void helper_iret_real(int shift)
{
......@@ -1051,7 +1067,7 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
{
uint32_t sp, new_cs, new_eip, new_eflags, new_esp, new_ss;
uint32_t new_es, new_ds, new_fs, new_gs;
uint32_t e1, e2;
uint32_t e1, e2, ss_e1, ss_e2;
int cpl, dpl, rpl, eflags_mask;
uint8_t *ssp;
......@@ -1098,7 +1114,10 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
if (rpl == cpl) {
/* return to same priledge level */
load_seg(R_CS, new_cs, env->eip);
cpu_x86_load_seg_cache(env, R_CS, new_cs,
get_seg_base(e1, e2),
get_seg_limit(e1, e2),
e2);
new_esp = sp + (4 << shift) + ((2 * is_iret) << shift) + addend;
} else {
/* return to different priviledge level */
......@@ -1115,20 +1134,27 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
if ((new_ss & 3) != rpl)
raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
if (load_segment(&e1, &e2, new_ss) != 0)
if (load_segment(&ss_e1, &ss_e2, new_ss) != 0)
raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
if (!(e2 & DESC_S_MASK) ||
(e2 & DESC_CS_MASK) ||
!(e2 & DESC_W_MASK))
if (!(ss_e2 & DESC_S_MASK) ||
(ss_e2 & DESC_CS_MASK) ||
!(ss_e2 & DESC_W_MASK))
raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
dpl = (e2 >> DESC_DPL_SHIFT) & 3;
dpl = (ss_e2 >> DESC_DPL_SHIFT) & 3;
if (dpl != rpl)
raise_exception_err(EXCP0D_GPF, new_ss & 0xfffc);
if (!(e2 & DESC_P_MASK))
if (!(ss_e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, new_ss & 0xfffc);
load_seg(R_CS, new_cs, env->eip);
load_seg(R_SS, new_ss, env->eip);
cpu_x86_load_seg_cache(env, R_CS, new_cs,
get_seg_base(e1, e2),
get_seg_limit(e1, e2),
e2);
cpu_x86_load_seg_cache(env, R_SS, new_ss,
get_seg_base(ss_e1, ss_e2),
get_seg_limit(ss_e1, ss_e2),
ss_e2);
cpu_x86_set_cpl(env, rpl);
}
if (env->segs[R_SS].flags & DESC_B_MASK)
ESP = new_esp;
......@@ -1137,6 +1163,7 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
(new_esp & 0xffff);
env->eip = new_eip;
if (is_iret) {
/* NOTE: 'cpl' can be different from the current CPL */
if (cpl == 0)
eflags_mask = FL_UPDATE_CPL0_MASK;
else
......
......@@ -433,6 +433,8 @@ int main(int argc, char **argv)
env->user_mode_only = 1;
#if defined(TARGET_I386)
cpu_x86_set_cpl(env, 3);
env->cr[0] = CR0_PG_MASK | CR0_WP_MASK | CR0_PE_MASK;
/* linux register setup */
......
......@@ -890,6 +890,7 @@ void OPPROTO op_das(void)
/* segment handling */
/* never use it with R_CS */
void OPPROTO op_movl_seg_T0(void)
{
load_seg(PARAM1, T0 & 0xffff, PARAM2);
......
......@@ -1544,7 +1544,8 @@ static void gen_setcc(DisasContext *s, int b)
}
}
/* move T0 to seg_reg and compute if the CPU state may change */
/* move T0 to seg_reg and compute if the CPU state may change. Never
call this function with seg_reg == R_CS */
static void gen_movl_seg_T0(DisasContext *s, int seg_reg, unsigned int cur_eip)
{
if (s->pe && !s->vm86)
......
......@@ -3590,12 +3590,12 @@ int main(int argc, char **argv)
env->gdt.base = (void *)params->gdt_table;
env->gdt.limit = sizeof(params->gdt_table) - 1;
cpu_x86_load_seg(env, R_CS, KERNEL_CS);
cpu_x86_load_seg(env, R_DS, KERNEL_DS);
cpu_x86_load_seg(env, R_ES, KERNEL_DS);
cpu_x86_load_seg(env, R_SS, KERNEL_DS);
cpu_x86_load_seg(env, R_FS, KERNEL_DS);
cpu_x86_load_seg(env, R_GS, KERNEL_DS);
cpu_x86_load_seg_cache(env, R_CS, KERNEL_CS, NULL, 0xffffffff, 0x00cf9a00);
cpu_x86_load_seg_cache(env, R_DS, KERNEL_DS, NULL, 0xffffffff, 0x00cf9200);
cpu_x86_load_seg_cache(env, R_ES, KERNEL_DS, NULL, 0xffffffff, 0x00cf9200);
cpu_x86_load_seg_cache(env, R_SS, KERNEL_DS, NULL, 0xffffffff, 0x00cf9200);
cpu_x86_load_seg_cache(env, R_FS, KERNEL_DS, NULL, 0xffffffff, 0x00cf9200);
cpu_x86_load_seg_cache(env, R_GS, KERNEL_DS, NULL, 0xffffffff, 0x00cf9200);
env->eip = KERNEL_LOAD_ADDR;
env->regs[R_ESI] = KERNEL_PARAMS_ADDR;
......@@ -3627,12 +3627,12 @@ int main(int argc, char **argv)
env->ldt.limit = 0xffff;
/* not correct (CS base=0xffff0000) */
cpu_x86_load_seg(env, R_CS, 0xf000);
cpu_x86_load_seg(env, R_DS, 0);
cpu_x86_load_seg(env, R_ES, 0);
cpu_x86_load_seg(env, R_SS, 0);
cpu_x86_load_seg(env, R_FS, 0);
cpu_x86_load_seg(env, R_GS, 0);
cpu_x86_load_seg_cache(env, R_CS, 0xf000, (uint8_t *)0x000f0000, 0xffff, 0);
cpu_x86_load_seg_cache(env, R_DS, 0, NULL, 0xffff, 0);
cpu_x86_load_seg_cache(env, R_ES, 0, NULL, 0xffff, 0);
cpu_x86_load_seg_cache(env, R_SS, 0, NULL, 0xffff, 0);
cpu_x86_load_seg_cache(env, R_FS, 0, NULL, 0xffff, 0);
cpu_x86_load_seg_cache(env, R_GS, 0, NULL, 0xffff, 0);
env->eip = 0xfff0;
env->regs[R_EDX] = 0x600; /* indicate P6 processor */
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment