/* XXX: restore CPU state in registers (PowerPC case) */
static void switch_tss(int tss_selector,
- uint32_t e1, uint32_t e2, int source)
+ uint32_t e1, uint32_t e2, int source,
+ uint32_t next_eip)
{
int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i;
uint8_t *tss_base;
if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) {
uint8_t *ptr;
uint32_t e2;
- ptr = env->gdt.base + (env->tr.selector << 3);
+ ptr = env->gdt.base + (env->tr.selector & ~7);
e2 = ldl_kernel(ptr + 4);
e2 &= ~DESC_TSS_BUSY_MASK;
stl_kernel(ptr + 4, e2);
/* save the current state in the old TSS */
if (type & 8) {
/* 32 bit */
- stl_kernel(env->tr.base + 0x20, env->eip);
+ stl_kernel(env->tr.base + 0x20, next_eip);
stl_kernel(env->tr.base + 0x24, old_eflags);
for(i = 0; i < 8; i++)
stl_kernel(env->tr.base + (0x28 + i * 4), env->regs[i]);
stw_kernel(env->tr.base + (0x48 + i * 4), env->segs[i].selector);
} else {
/* 16 bit */
- stw_kernel(env->tr.base + 0x0e, new_eip);
+ stw_kernel(env->tr.base + 0x0e, next_eip);
stw_kernel(env->tr.base + 0x10, old_eflags);
for(i = 0; i < 8; i++)
stw_kernel(env->tr.base + (0x12 + i * 2), env->regs[i]);
if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) {
uint8_t *ptr;
uint32_t e2;
- ptr = env->gdt.base + (tss_selector << 3);
+ ptr = env->gdt.base + (tss_selector & ~7);
e2 = ldl_kernel(ptr + 4);
e2 |= DESC_TSS_BUSY_MASK;
stl_kernel(ptr + 4, e2);
/* set the new CPU state */
/* from this point, any exception which occurs can give problems */
env->cr[0] |= CR0_TS_MASK;
+ env->hflags |= HF_TS_MASK;
env->tr.selector = tss_selector;
env->tr.base = tss_base;
env->tr.limit = tss_limit;
/* check that EIP is in the CS segment limits */
if (new_eip > env->segs[R_CS].limit) {
+ /* XXX: different exception if CALL ? */
raise_exception_err(EXCP0D_GPF, 0);
}
}
break;
}
}
+ if (is_int)
+ old_eip = next_eip;
+ else
+ old_eip = env->eip;
dt = &env->idt;
if (intno * 8 + 7 > dt->limit)
/* must do that check here to return the correct error code */
if (!(e2 & DESC_P_MASK))
raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
- switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL);
+ switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip);
if (has_error_code) {
int mask;
/* push the error code */
push_size += 8;
push_size <<= shift;
#endif
- if (is_int)
- old_eip = next_eip;
- else
- old_eip = env->eip;
if (shift == 1) {
if (new_stack) {
if (env->eflags & VM_MASK) {
case 5: /* task gate */
if (dpl < cpl || dpl < rpl)
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
- switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP);
+ /* XXX: check if it is really the current EIP */
+ switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, env->eip);
break;
case 4: /* 286 call gate */
case 12: /* 386 call gate */
case 5: /* task gate */
if (dpl < cpl || dpl < rpl)
raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc);
- switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL);
+ switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip);
return;
case 4: /* 286 call gate */
case 12: /* 386 call gate */
/* NOTE: we check both segment and busy TSS */
if (type != 3)
raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc);
- switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET);
+ /* XXX: check if it is really the current EIP */
+ switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, env->eip);
} else {
helper_ret_protected(shift, 1, 0);
}