aboutsummaryrefslogtreecommitdiff
path: root/target-i386
diff options
context:
space:
mode:
Diffstat (limited to 'target-i386')
-rw-r--r--target-i386/helper.c388
1 files changed, 188 insertions, 200 deletions
diff --git a/target-i386/helper.c b/target-i386/helper.c
index 7fd9a9a8f3..861266fa14 100644
--- a/target-i386/helper.c
+++ b/target-i386/helper.c
@@ -531,16 +531,49 @@ void check_iol_DX(void)
check_io(EDX & 0xffff, 4);
}
+static inline unsigned int get_sp_mask(unsigned int e2)
+{
+ if (e2 & DESC_B_MASK)
+ return 0xffffffff;
+ else
+ return 0xffff;
+}
+
+/* XXX: add a is_user flag to have proper security support */
+#define PUSHW(ssp, sp, sp_mask, val)\
+{\
+ sp -= 2;\
+ stw_kernel((ssp) + (sp & (sp_mask)), (val));\
+}
+
+#define PUSHL(ssp, sp, sp_mask, val)\
+{\
+ sp -= 4;\
+ stl_kernel((ssp) + (sp & (sp_mask)), (val));\
+}
+
+#define POPW(ssp, sp, sp_mask, val)\
+{\
+ val = lduw_kernel((ssp) + (sp & (sp_mask)));\
+ sp += 2;\
+}
+
+#define POPL(ssp, sp, sp_mask, val)\
+{\
+ val = ldl_kernel((ssp) + (sp & (sp_mask)));\
+ sp += 4;\
+}
+
/* protected mode interrupt */
static void do_interrupt_protected(int intno, int is_int, int error_code,
unsigned int next_eip, int is_hw)
{
SegmentCache *dt;
uint8_t *ptr, *ssp;
- int type, dpl, selector, ss_dpl, cpl;
+ int type, dpl, selector, ss_dpl, cpl, sp_mask;
int has_error_code, new_stack, shift;
- uint32_t e1, e2, offset, ss, esp, ss_e1, ss_e2, push_size;
- uint32_t old_cs, old_ss, old_esp, old_eip;
+ uint32_t e1, e2, offset, ss, esp, ss_e1, ss_e2;
+ uint32_t old_eip;
#ifdef DEBUG_PCALL
if (loglevel) {
@@ -659,96 +692,80 @@ static void do_interrupt_protected(int intno, int is_int, int error_code,
if (!(ss_e2 & DESC_P_MASK))
raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
new_stack = 1;
+ sp_mask = get_sp_mask(ss_e2);
+ ssp = get_seg_base(ss_e1, ss_e2);
} else if ((e2 & DESC_C_MASK) || dpl == cpl) {
/* to same priviledge */
new_stack = 0;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ esp = ESP;
} else {
raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
new_stack = 0; /* avoid warning */
+ sp_mask = 0; /* avoid warning */
+ ssp = NULL; /* avoid warning */
+ esp = 0; /* avoid warning */
}
shift = type >> 3;
+
+#if 0
+ /* XXX: check that enough room is available */
push_size = 6 + (new_stack << 2) + (has_error_code << 1);
if (env->eflags & VM_MASK)
push_size += 8;
push_size <<= shift;
-
- /* XXX: check that enough room is available */
- if (new_stack) {
- old_esp = ESP;
- old_ss = env->segs[R_SS].selector;
- 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;
- esp = ESP;
- }
+#endif
if (is_int)
old_eip = next_eip;
else
old_eip = env->eip;
- old_cs = env->segs[R_CS].selector;
- 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;
if (shift == 1) {
- int old_eflags;
if (env->eflags & VM_MASK) {
- ssp -= 4;
- stl_kernel(ssp, env->segs[R_GS].selector);
- ssp -= 4;
- stl_kernel(ssp, env->segs[R_FS].selector);
- ssp -= 4;
- stl_kernel(ssp, env->segs[R_DS].selector);
- ssp -= 4;
- stl_kernel(ssp, env->segs[R_ES].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_GS].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_FS].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_DS].selector);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_ES].selector);
}
if (new_stack) {
- ssp -= 4;
- stl_kernel(ssp, old_ss);
- ssp -= 4;
- stl_kernel(ssp, old_esp);
+ PUSHL(ssp, esp, sp_mask, env->segs[R_SS].selector);
+ PUSHL(ssp, esp, sp_mask, ESP);
}
- ssp -= 4;
- old_eflags = compute_eflags();
- stl_kernel(ssp, old_eflags);
- ssp -= 4;
- stl_kernel(ssp, old_cs);
- ssp -= 4;
- stl_kernel(ssp, old_eip);
+ PUSHL(ssp, esp, sp_mask, compute_eflags());
+ PUSHL(ssp, esp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, esp, sp_mask, old_eip);
if (has_error_code) {
- ssp -= 4;
- stl_kernel(ssp, error_code);
+ PUSHL(ssp, esp, sp_mask, error_code);
}
} else {
if (new_stack) {
- ssp -= 2;
- stw_kernel(ssp, old_ss);
- ssp -= 2;
- stw_kernel(ssp, old_esp);
+ PUSHW(ssp, esp, sp_mask, env->segs[R_SS].selector);
+ PUSHW(ssp, esp, sp_mask, ESP);
}
- ssp -= 2;
- stw_kernel(ssp, compute_eflags());
- ssp -= 2;
- stw_kernel(ssp, old_cs);
- ssp -= 2;
- stw_kernel(ssp, old_eip);
+ PUSHW(ssp, esp, sp_mask, compute_eflags());
+ PUSHW(ssp, esp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, esp, sp_mask, old_eip);
if (has_error_code) {
- ssp -= 2;
- stw_kernel(ssp, error_code);
+ PUSHW(ssp, esp, sp_mask, error_code);
}
}
+ if (new_stack) {
+ ss = (ss & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_SS, ss,
+ ssp, get_seg_limit(ss_e1, ss_e2), ss_e2);
+ }
+ ESP = (ESP & ~sp_mask) | (esp & sp_mask);
+
+ 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;
+
/* interrupt gate clear IF mask */
if ((type & 1) == 0) {
env->eflags &= ~IF_MASK;
@@ -780,12 +797,10 @@ static void do_interrupt_real(int intno, int is_int, int error_code,
else
old_eip = env->eip;
old_cs = env->segs[R_CS].selector;
- esp -= 2;
- stw_kernel(ssp + (esp & 0xffff), compute_eflags());
- esp -= 2;
- stw_kernel(ssp + (esp & 0xffff), old_cs);
- esp -= 2;
- stw_kernel(ssp + (esp & 0xffff), old_eip);
+ /* XXX: use SS segment size ? */
+ PUSHW(ssp, esp, 0xffff, compute_eflags());
+ PUSHW(ssp, esp, 0xffff, old_cs);
+ PUSHW(ssp, esp, 0xffff, old_eip);
/* update processor state */
ESP = (ESP & ~0xffff) | (esp & 0xffff);
@@ -1247,26 +1262,17 @@ void helper_lcall_real_T0_T1(int shift, int next_eip)
new_cs = T0;
new_eip = T1;
esp = ESP;
- esp_mask = 0xffffffff;
- if (!(env->segs[R_SS].flags & DESC_B_MASK))
- esp_mask = 0xffff;
+ esp_mask = get_sp_mask(env->segs[R_SS].flags);
ssp = env->segs[R_SS].base;
if (shift) {
- esp -= 4;
- stl_kernel(ssp + (esp & esp_mask), env->segs[R_CS].selector);
- esp -= 4;
- stl_kernel(ssp + (esp & esp_mask), next_eip);
+ PUSHL(ssp, esp, esp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, esp, esp_mask, next_eip);
} else {
- esp -= 2;
- stw_kernel(ssp + (esp & esp_mask), env->segs[R_CS].selector);
- esp -= 2;
- stw_kernel(ssp + (esp & esp_mask), next_eip);
+ PUSHW(ssp, esp, esp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, esp, esp_mask, next_eip);
}
- if (!(env->segs[R_SS].flags & DESC_B_MASK))
- ESP = (ESP & ~0xffff) | (esp & 0xffff);
- else
- ESP = esp;
+ ESP = (ESP & ~esp_mask) | (esp & esp_mask);
env->eip = new_eip;
env->segs[R_CS].selector = new_cs;
env->segs[R_CS].base = (uint8_t *)(new_cs << 4);
@@ -1275,10 +1281,10 @@ void helper_lcall_real_T0_T1(int shift, int next_eip)
/* protected mode call */
void helper_lcall_protected_T0_T1(int shift, int next_eip)
{
- int new_cs, new_eip;
+ int new_cs, new_eip, new_stack, i;
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, limit;
+ uint32_t ss, ss_e1, ss_e2, sp, type, ss_dpl, sp_mask;
+ uint32_t val, limit, old_sp_mask;
uint8_t *ssp, *old_ssp;
new_cs = T0;
@@ -1319,30 +1325,21 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip)
raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
sp = ESP;
- if (!(env->segs[R_SS].flags & DESC_B_MASK))
- sp &= 0xffff;
- ssp = env->segs[R_SS].base + sp;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
if (shift) {
- ssp -= 4;
- stl_kernel(ssp, env->segs[R_CS].selector);
- ssp -= 4;
- stl_kernel(ssp, next_eip);
+ PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, sp, sp_mask, next_eip);
} else {
- ssp -= 2;
- stw_kernel(ssp, env->segs[R_CS].selector);
- ssp -= 2;
- stw_kernel(ssp, next_eip);
+ PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, sp, sp_mask, next_eip);
}
- sp -= (4 << shift);
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;
+ ESP = (ESP & ~sp_mask) | (sp & sp_mask);
cpu_x86_load_seg_cache(env, R_CS, (new_cs & 0xfffc) | cpl,
get_seg_base(e1, e2), limit, e2);
EIP = new_eip;
@@ -1413,77 +1410,63 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip)
if (!(ss_e2 & DESC_P_MASK))
raise_exception_err(EXCP0A_TSS, ss & 0xfffc);
- push_size = ((param_count * 2) + 8) << shift;
+ // push_size = ((param_count * 2) + 8) << shift;
- old_esp = ESP;
- old_ss = env->segs[R_SS].selector;
- if (!(env->segs[R_SS].flags & DESC_B_MASK))
- old_esp &= 0xffff;
- old_ssp = env->segs[R_SS].base + old_esp;
+ old_sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ old_ssp = env->segs[R_SS].base;
- /* XXX: from this point not restartable */
- 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 (!(ss_e2 & DESC_B_MASK))
- sp &= 0xffff;
- ssp = env->segs[R_SS].base + sp;
+ sp_mask = get_sp_mask(ss_e2);
+ ssp = get_seg_base(ss_e1, ss_e2);
if (shift) {
- ssp -= 4;
- stl_kernel(ssp, old_ss);
- ssp -= 4;
- stl_kernel(ssp, old_esp);
- ssp -= 4 * param_count;
- for(i = 0; i < param_count; i++) {
- val = ldl_kernel(old_ssp + i * 4);
- stl_kernel(ssp + i * 4, val);
+ PUSHL(ssp, sp, sp_mask, env->segs[R_SS].selector);
+ PUSHL(ssp, sp, sp_mask, ESP);
+ for(i = param_count - 1; i >= 0; i--) {
+ val = ldl_kernel(old_ssp + ((ESP + i * 4) & old_sp_mask));
+ PUSHL(ssp, sp, sp_mask, val);
}
} else {
- ssp -= 2;
- stw_kernel(ssp, old_ss);
- ssp -= 2;
- stw_kernel(ssp, old_esp);
- ssp -= 2 * param_count;
- for(i = 0; i < param_count; i++) {
- val = lduw_kernel(old_ssp + i * 2);
- stw_kernel(ssp + i * 2, val);
+ PUSHW(ssp, sp, sp_mask, env->segs[R_SS].selector);
+ PUSHW(ssp, sp, sp_mask, ESP);
+ for(i = param_count - 1; i >= 0; i--) {
+ val = lduw_kernel(old_ssp + ((ESP + i * 2) & old_sp_mask));
+ PUSHW(ssp, sp, sp_mask, val);
}
}
+ new_stack = 1;
} else {
/* to same priviledge */
- if (!(env->segs[R_SS].flags & DESC_B_MASK))
- sp &= 0xffff;
- ssp = env->segs[R_SS].base + sp;
- push_size = (4 << shift);
+ sp = ESP;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
+ ssp = env->segs[R_SS].base;
+ // push_size = (4 << shift);
+ new_stack = 0;
}
if (shift) {
- ssp -= 4;
- stl_kernel(ssp, env->segs[R_CS].selector);
- ssp -= 4;
- stl_kernel(ssp, next_eip);
+ PUSHL(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHL(ssp, sp, sp_mask, next_eip);
} else {
- ssp -= 2;
- stw_kernel(ssp, env->segs[R_CS].selector);
- ssp -= 2;
- stw_kernel(ssp, next_eip);
+ PUSHW(ssp, sp, sp_mask, env->segs[R_CS].selector);
+ PUSHW(ssp, sp, sp_mask, next_eip);
+ }
+
+ /* from this point, not restartable */
+
+ if (new_stack) {
+ ss = (ss & ~3) | dpl;
+ cpu_x86_load_seg_cache(env, R_SS, ss,
+ ssp,
+ get_seg_limit(ss_e1, ss_e2),
+ ss_e2);
}
- sp -= push_size;
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);
-
- if (!(env->segs[R_SS].flags & DESC_B_MASK))
- ESP = (ESP & 0xffff0000) | (sp & 0xffff);
- else
- ESP = sp;
+ ESP = (ESP & ~sp_mask) | (sp & sp_mask);
EIP = offset;
}
}
@@ -1491,26 +1474,26 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip)
/* real and vm86 mode iret */
void helper_iret_real(int shift)
{
- uint32_t sp, new_cs, new_eip, new_eflags, new_esp;
+ uint32_t sp, new_cs, new_eip, new_eflags, sp_mask;
uint8_t *ssp;
int eflags_mask;
- sp = ESP & 0xffff;
- ssp = env->segs[R_SS].base + sp;
+ sp_mask = 0xffff; /* XXXX: use SS segment size ? */
+ sp = ESP;
+ ssp = env->segs[R_SS].base;
if (shift == 1) {
/* 32 bits */
- new_eflags = ldl_kernel(ssp + 8);
- new_cs = ldl_kernel(ssp + 4) & 0xffff;
- new_eip = ldl_kernel(ssp) & 0xffff;
+ POPL(ssp, sp, sp_mask, new_eip);
+ POPL(ssp, sp, sp_mask, new_cs);
+ new_cs &= 0xffff;
+ POPL(ssp, sp, sp_mask, new_eflags);
} else {
/* 16 bits */
- new_eflags = lduw_kernel(ssp + 4);
- new_cs = lduw_kernel(ssp + 2);
- new_eip = lduw_kernel(ssp);
+ POPW(ssp, sp, sp_mask, new_eip);
+ POPW(ssp, sp, sp_mask, new_cs);
+ POPW(ssp, sp, sp_mask, new_eflags);
}
- new_esp = sp + (6 << shift);
- ESP = (ESP & 0xffff0000) |
- (new_esp & 0xffff);
+ ESP = (ESP & ~sp_mask) | (sp & 0xffff);
load_seg_vm(R_CS, new_cs);
env->eip = new_eip;
if (env->eflags & VM_MASK)
@@ -1525,31 +1508,38 @@ void helper_iret_real(int shift)
/* protected mode iret */
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 sp, new_cs, new_eip, new_eflags, new_esp, new_ss, sp_mask;
uint32_t new_es, new_ds, new_fs, new_gs;
uint32_t e1, e2, ss_e1, ss_e2;
int cpl, dpl, rpl, eflags_mask;
uint8_t *ssp;
+ sp_mask = get_sp_mask(env->segs[R_SS].flags);
sp = ESP;
- if (!(env->segs[R_SS].flags & DESC_B_MASK))
- sp &= 0xffff;
- ssp = env->segs[R_SS].base + sp;
+ ssp = env->segs[R_SS].base;
if (shift == 1) {
/* 32 bits */
- if (is_iret)
- new_eflags = ldl_kernel(ssp + 8);
- new_cs = ldl_kernel(ssp + 4) & 0xffff;
- new_eip = ldl_kernel(ssp);
- if (is_iret && (new_eflags & VM_MASK))
- goto return_to_vm86;
+ POPL(ssp, sp, sp_mask, new_eip);
+ POPL(ssp, sp, sp_mask, new_cs);
+ new_cs &= 0xffff;
+ if (is_iret) {
+ POPL(ssp, sp, sp_mask, new_eflags);
+ if (new_eflags & VM_MASK)
+ goto return_to_vm86;
+ }
} else {
/* 16 bits */
+ POPW(ssp, sp, sp_mask, new_eip);
+ POPW(ssp, sp, sp_mask, new_cs);
if (is_iret)
- new_eflags = lduw_kernel(ssp + 4);
- new_cs = lduw_kernel(ssp + 2);
- new_eip = lduw_kernel(ssp);
+ POPW(ssp, sp, sp_mask, new_eflags);
}
+#ifdef DEBUG_PCALL
+ if (loglevel) {
+ fprintf(logfile, "lret new %04x:%08x\n",
+ new_cs, new_eip);
+ }
+#endif
if ((new_cs & 0xfffc) == 0)
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
if (load_segment(&e1, &e2, new_cs) != 0)
@@ -1572,24 +1562,24 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, new_cs & 0xfffc);
+ sp += addend;
if (rpl == cpl) {
/* return to same priledge level */
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 */
- ssp += (4 << shift) + ((2 * is_iret) << shift) + addend;
if (shift == 1) {
/* 32 bits */
- new_esp = ldl_kernel(ssp);
- new_ss = ldl_kernel(ssp + 4) & 0xffff;
+ POPL(ssp, sp, sp_mask, new_esp);
+ POPL(ssp, sp, sp_mask, new_ss);
+ new_ss &= 0xffff;
} else {
/* 16 bits */
- new_esp = lduw_kernel(ssp);
- new_ss = lduw_kernel(ssp + 2);
+ POPW(ssp, sp, sp_mask, new_esp);
+ POPW(ssp, sp, sp_mask, new_ss);
}
if ((new_ss & 3) != rpl)
@@ -1615,12 +1605,10 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
get_seg_limit(ss_e1, ss_e2),
ss_e2);
cpu_x86_set_cpl(env, rpl);
+ sp = new_esp;
+ /* XXX: change sp_mask according to old segment ? */
}
- if (env->segs[R_SS].flags & DESC_B_MASK)
- ESP = new_esp;
- else
- ESP = (ESP & 0xffff0000) |
- (new_esp & 0xffff);
+ ESP = (ESP & ~sp_mask) | (sp & sp_mask);
env->eip = new_eip;
if (is_iret) {
/* NOTE: 'cpl' can be different from the current CPL */
@@ -1635,22 +1623,22 @@ static inline void helper_ret_protected(int shift, int is_iret, int addend)
return;
return_to_vm86:
- new_esp = ldl_kernel(ssp + 12);
- new_ss = ldl_kernel(ssp + 16);
- new_es = ldl_kernel(ssp + 20);
- new_ds = ldl_kernel(ssp + 24);
- new_fs = ldl_kernel(ssp + 28);
- new_gs = ldl_kernel(ssp + 32);
+ POPL(ssp, sp, sp_mask, new_esp);
+ POPL(ssp, sp, sp_mask, new_ss);
+ POPL(ssp, sp, sp_mask, new_es);
+ POPL(ssp, sp, sp_mask, new_ds);
+ POPL(ssp, sp, sp_mask, new_fs);
+ POPL(ssp, sp, sp_mask, new_gs);
/* modify processor state */
load_eflags(new_eflags, FL_UPDATE_CPL0_MASK | VM_MASK | VIF_MASK | VIP_MASK);
- load_seg_vm(R_CS, new_cs);
+ load_seg_vm(R_CS, new_cs & 0xffff);
cpu_x86_set_cpl(env, 3);
- load_seg_vm(R_SS, new_ss);
- load_seg_vm(R_ES, new_es);
- load_seg_vm(R_DS, new_ds);
- load_seg_vm(R_FS, new_fs);
- load_seg_vm(R_GS, new_gs);
+ load_seg_vm(R_SS, new_ss & 0xffff);
+ load_seg_vm(R_ES, new_es & 0xffff);
+ load_seg_vm(R_DS, new_ds & 0xffff);
+ load_seg_vm(R_FS, new_fs & 0xffff);
+ load_seg_vm(R_GS, new_gs & 0xffff);
env->eip = new_eip;
ESP = new_esp;