Commit 883da8e21932b24630f87ed4d801ea1ad48b735b
1 parent
78ebca6e
task switch fixes
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@681 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
18 additions
and
13 deletions
target-i386/helper.c
| ... | ... | @@ -277,7 +277,8 @@ static void tss_load_seg(int seg_reg, int selector) |
| 277 | 277 | |
| 278 | 278 | /* XXX: restore CPU state in registers (PowerPC case) */ |
| 279 | 279 | static void switch_tss(int tss_selector, |
| 280 | - uint32_t e1, uint32_t e2, int source) | |
| 280 | + uint32_t e1, uint32_t e2, int source, | |
| 281 | + uint32_t next_eip) | |
| 281 | 282 | { |
| 282 | 283 | int tss_limit, tss_limit_max, type, old_tss_limit_max, old_type, v1, v2, i; |
| 283 | 284 | uint8_t *tss_base; |
| ... | ... | @@ -369,7 +370,7 @@ static void switch_tss(int tss_selector, |
| 369 | 370 | if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_IRET) { |
| 370 | 371 | uint8_t *ptr; |
| 371 | 372 | uint32_t e2; |
| 372 | - ptr = env->gdt.base + (env->tr.selector << 3); | |
| 373 | + ptr = env->gdt.base + (env->tr.selector & ~7); | |
| 373 | 374 | e2 = ldl_kernel(ptr + 4); |
| 374 | 375 | e2 &= ~DESC_TSS_BUSY_MASK; |
| 375 | 376 | stl_kernel(ptr + 4, e2); |
| ... | ... | @@ -381,7 +382,7 @@ static void switch_tss(int tss_selector, |
| 381 | 382 | /* save the current state in the old TSS */ |
| 382 | 383 | if (type & 8) { |
| 383 | 384 | /* 32 bit */ |
| 384 | - stl_kernel(env->tr.base + 0x20, env->eip); | |
| 385 | + stl_kernel(env->tr.base + 0x20, next_eip); | |
| 385 | 386 | stl_kernel(env->tr.base + 0x24, old_eflags); |
| 386 | 387 | for(i = 0; i < 8; i++) |
| 387 | 388 | stl_kernel(env->tr.base + (0x28 + i * 4), env->regs[i]); |
| ... | ... | @@ -389,7 +390,7 @@ static void switch_tss(int tss_selector, |
| 389 | 390 | stw_kernel(env->tr.base + (0x48 + i * 4), env->segs[i].selector); |
| 390 | 391 | } else { |
| 391 | 392 | /* 16 bit */ |
| 392 | - stw_kernel(env->tr.base + 0x0e, new_eip); | |
| 393 | + stw_kernel(env->tr.base + 0x0e, next_eip); | |
| 393 | 394 | stw_kernel(env->tr.base + 0x10, old_eflags); |
| 394 | 395 | for(i = 0; i < 8; i++) |
| 395 | 396 | stw_kernel(env->tr.base + (0x12 + i * 2), env->regs[i]); |
| ... | ... | @@ -409,7 +410,7 @@ static void switch_tss(int tss_selector, |
| 409 | 410 | if (source == SWITCH_TSS_JMP || source == SWITCH_TSS_CALL) { |
| 410 | 411 | uint8_t *ptr; |
| 411 | 412 | uint32_t e2; |
| 412 | - ptr = env->gdt.base + (tss_selector << 3); | |
| 413 | + ptr = env->gdt.base + (tss_selector & ~7); | |
| 413 | 414 | e2 = ldl_kernel(ptr + 4); |
| 414 | 415 | e2 |= DESC_TSS_BUSY_MASK; |
| 415 | 416 | stl_kernel(ptr + 4, e2); |
| ... | ... | @@ -418,6 +419,7 @@ static void switch_tss(int tss_selector, |
| 418 | 419 | /* set the new CPU state */ |
| 419 | 420 | /* from this point, any exception which occurs can give problems */ |
| 420 | 421 | env->cr[0] |= CR0_TS_MASK; |
| 422 | + env->hflags |= HF_TS_MASK; | |
| 421 | 423 | env->tr.selector = tss_selector; |
| 422 | 424 | env->tr.base = tss_base; |
| 423 | 425 | env->tr.limit = tss_limit; |
| ... | ... | @@ -486,6 +488,7 @@ static void switch_tss(int tss_selector, |
| 486 | 488 | |
| 487 | 489 | /* check that EIP is in the CS segment limits */ |
| 488 | 490 | if (new_eip > env->segs[R_CS].limit) { |
| 491 | + /* XXX: different exception if CALL ? */ | |
| 489 | 492 | raise_exception_err(EXCP0D_GPF, 0); |
| 490 | 493 | } |
| 491 | 494 | } |
| ... | ... | @@ -603,6 +606,10 @@ static void do_interrupt_protected(int intno, int is_int, int error_code, |
| 603 | 606 | break; |
| 604 | 607 | } |
| 605 | 608 | } |
| 609 | + if (is_int) | |
| 610 | + old_eip = next_eip; | |
| 611 | + else | |
| 612 | + old_eip = env->eip; | |
| 606 | 613 | |
| 607 | 614 | dt = &env->idt; |
| 608 | 615 | if (intno * 8 + 7 > dt->limit) |
| ... | ... | @@ -617,7 +624,7 @@ static void do_interrupt_protected(int intno, int is_int, int error_code, |
| 617 | 624 | /* must do that check here to return the correct error code */ |
| 618 | 625 | if (!(e2 & DESC_P_MASK)) |
| 619 | 626 | raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2); |
| 620 | - switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL); | |
| 627 | + switch_tss(intno * 8, e1, e2, SWITCH_TSS_CALL, old_eip); | |
| 621 | 628 | if (has_error_code) { |
| 622 | 629 | int mask; |
| 623 | 630 | /* push the error code */ |
| ... | ... | @@ -713,10 +720,6 @@ static void do_interrupt_protected(int intno, int is_int, int error_code, |
| 713 | 720 | push_size += 8; |
| 714 | 721 | push_size <<= shift; |
| 715 | 722 | #endif |
| 716 | - if (is_int) | |
| 717 | - old_eip = next_eip; | |
| 718 | - else | |
| 719 | - old_eip = env->eip; | |
| 720 | 723 | if (shift == 1) { |
| 721 | 724 | if (new_stack) { |
| 722 | 725 | if (env->eflags & VM_MASK) { |
| ... | ... | @@ -1264,7 +1267,8 @@ void helper_ljmp_protected_T0_T1(void) |
| 1264 | 1267 | case 5: /* task gate */ |
| 1265 | 1268 | if (dpl < cpl || dpl < rpl) |
| 1266 | 1269 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); |
| 1267 | - switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP); | |
| 1270 | + /* XXX: check if it is really the current EIP */ | |
| 1271 | + switch_tss(new_cs, e1, e2, SWITCH_TSS_JMP, env->eip); | |
| 1268 | 1272 | break; |
| 1269 | 1273 | case 4: /* 286 call gate */ |
| 1270 | 1274 | case 12: /* 386 call gate */ |
| ... | ... | @@ -1405,7 +1409,7 @@ void helper_lcall_protected_T0_T1(int shift, int next_eip) |
| 1405 | 1409 | case 5: /* task gate */ |
| 1406 | 1410 | if (dpl < cpl || dpl < rpl) |
| 1407 | 1411 | raise_exception_err(EXCP0D_GPF, new_cs & 0xfffc); |
| 1408 | - switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL); | |
| 1412 | + switch_tss(new_cs, e1, e2, SWITCH_TSS_CALL, next_eip); | |
| 1409 | 1413 | return; |
| 1410 | 1414 | case 4: /* 286 call gate */ |
| 1411 | 1415 | case 12: /* 386 call gate */ |
| ... | ... | @@ -1744,7 +1748,8 @@ void helper_iret_protected(int shift) |
| 1744 | 1748 | /* NOTE: we check both segment and busy TSS */ |
| 1745 | 1749 | if (type != 3) |
| 1746 | 1750 | raise_exception_err(EXCP0A_TSS, tss_selector & 0xfffc); |
| 1747 | - switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET); | |
| 1751 | + /* XXX: check if it is really the current EIP */ | |
| 1752 | + switch_tss(tss_selector, e1, e2, SWITCH_TSS_IRET, env->eip); | |
| 1748 | 1753 | } else { |
| 1749 | 1754 | helper_ret_protected(shift, 1, 0); |
| 1750 | 1755 | } | ... | ... |