Commit 883da8e21932b24630f87ed4d801ea1ad48b735b

Authored by bellard
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 }
... ...