src/emu/cpu/i386/i386.c
| r15081 | r15082 | |
| 52 | 52 | entry = seg->selector & ~0x7; |
| 53 | 53 | if (limit == 0 || entry + 7 > limit) |
| 54 | 54 | return 0; |
| 55 | |
| 56 | int cpl = cpustate->CPL; |
| 57 | cpustate->CPL = 0; |
| 55 | 58 | |
| 56 | 59 | v1 = READ32(cpustate, base + entry ); |
| 57 | 60 | v2 = READ32(cpustate, base + entry + 4 ); |
| 58 | 61 | |
| 62 | cpustate->CPL = cpl; |
| 63 | |
| 59 | 64 | seg->flags = (v2 >> 8) & 0xf0ff; |
| 60 | 65 | seg->base = (v2 & 0xff000000) | ((v2 & 0xff) << 16) | ((v1 >> 16) & 0xffff); |
| 61 | 66 | seg->limit = (v2 & 0xf0000) | (v1 & 0xffff); |
| r15081 | r15082 | |
| 88 | 93 | if (limit == 0 || entry + 7 > limit) |
| 89 | 94 | return; |
| 90 | 95 | |
| 96 | int cpl = cpustate->CPL; |
| 97 | cpustate->CPL = 0; |
| 98 | |
| 91 | 99 | v1 = READ32(cpustate, base + entry ); |
| 92 | 100 | v2 = READ32(cpustate, base + entry + 4 ); |
| 93 | 101 | |
| 102 | cpustate->CPL = cpl; |
| 103 | |
| 94 | 104 | /* Note that for task gates, offset and dword_count are not used */ |
| 95 | 105 | gate->selector = (v1 >> 16) & 0xffff; |
| 96 | 106 | gate->offset = (v1 & 0x0000ffff) | (v2 & 0xffff0000); |
| r15081 | r15082 | |
| 129 | 139 | /* Retrieves the stack selector located in the current TSS */ |
| 130 | 140 | static UINT32 i386_get_stack_segment(i386_state* cpustate, UINT8 privilege) |
| 131 | 141 | { |
| 142 | UINT32 ret; |
| 132 | 143 | if(privilege >= 3) |
| 133 | 144 | return 0; |
| 134 | 145 | |
| 135 | | return READ32(cpustate,(cpustate->task.base+8) + (8*privilege)); |
| 136 | | } |
| 146 | int cpl = cpustate->CPL; |
| 147 | cpustate->CPL = 0; |
| 137 | 148 | |
| 138 | | static UINT16 i286_get_stack_segment(i386_state* cpustate, UINT8 privilege) |
| 139 | | { |
| 140 | | if(privilege >= 3) |
| 141 | | return 0; |
| 149 | if(cpustate->task.flags & 8) |
| 150 | ret = READ32(cpustate,(cpustate->task.base+8) + (8*privilege)); |
| 151 | else |
| 152 | ret = READ16(cpustate,(cpustate->task.base+4) + (4*privilege)); |
| 142 | 153 | |
| 143 | | return READ16(cpustate,(cpustate->task.base+4) + (4*privilege)); |
| 154 | cpustate->CPL = cpl; |
| 155 | return ret; |
| 144 | 156 | } |
| 145 | 157 | |
| 146 | 158 | /* Retrieves the stack pointer located in the current TSS */ |
| 147 | 159 | static UINT32 i386_get_stack_ptr(i386_state* cpustate, UINT8 privilege) |
| 148 | 160 | { |
| 161 | UINT32 ret; |
| 149 | 162 | if(privilege >= 3) |
| 150 | 163 | return 0; |
| 151 | 164 | |
| 152 | | return READ32(cpustate,(cpustate->task.base+4) + (8*privilege)); |
| 153 | | } |
| 165 | int cpl = cpustate->CPL; |
| 166 | cpustate->CPL = 0; |
| 154 | 167 | |
| 155 | | static UINT16 i286_get_stack_ptr(i386_state* cpustate, UINT8 privilege) |
| 156 | | { |
| 157 | | if(privilege >= 3) |
| 158 | | return 0; |
| 168 | if(cpustate->task.flags & 8) |
| 169 | ret = READ32(cpustate,(cpustate->task.base+4) + (8*privilege)); |
| 170 | else |
| 171 | ret = READ16(cpustate,(cpustate->task.base+2) + (4*privilege)); |
| 159 | 172 | |
| 160 | | return READ16(cpustate,(cpustate->task.base+2) + (4*privilege)); |
| 173 | cpustate->CPL = cpl; |
| 174 | return ret; |
| 161 | 175 | } |
| 162 | 176 | |
| 163 | 177 | static UINT32 get_flags(i386_state *cpustate) |
| r15081 | r15082 | |
| 596 | 610 | UINT8 CPL = cpustate->CPL, DPL = 0; //, RPL = 0; |
| 597 | 611 | |
| 598 | 612 | /* 32-bit */ |
| 613 | cpustate->CPL = 0; |
| 599 | 614 | v1 = READ32(cpustate, cpustate->idtr.base + entry ); |
| 600 | 615 | v2 = READ32(cpustate, cpustate->idtr.base + entry + 4 ); |
| 616 | cpustate->CPL = CPL; |
| 601 | 617 | offset = (v2 & 0xffff0000) | (v1 & 0xffff); |
| 602 | 618 | segment = (v1 >> 16) & 0xffff; |
| 603 | 619 | type = (v2>>8) & 0x1F; |
| r15081 | r15082 | |
| 661 | 677 | /* Get privilege level 0 stack pointer from TSS */ |
| 662 | 678 | cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,0); |
| 663 | 679 | REG32(ESP) = i386_get_stack_ptr(cpustate,0); |
| 680 | cpustate->CPL = segment & 0x03; |
| 664 | 681 | i386_load_segment_descriptor(cpustate,SS); |
| 665 | 682 | PUSH32(cpustate,cpustate->sreg[GS].selector & 0xffff); |
| 666 | 683 | PUSH32(cpustate,cpustate->sreg[FS].selector & 0xffff); |
| r15081 | r15082 | |
| 686 | 703 | cpustate->sreg[CS].selector = segment; |
| 687 | 704 | cpustate->eip = offset; |
| 688 | 705 | // CPL set to CS RPL? |
| 689 | | cpustate->CPL = segment & 0x03; |
| 690 | 706 | |
| 691 | 707 | i386_load_segment_descriptor(cpustate,CS); |
| 692 | 708 | CHANGE_PC(cpustate,cpustate->eip); |
| r15081 | r15082 | |
| 702 | 718 | if(segment & 0x04) |
| 703 | 719 | { |
| 704 | 720 | logerror("IRQ: Task gate: TSS is not in the GDT.\n"); |
| 705 | | FAULT_EXP(FAULT_TS,segment & ~0x07); |
| 721 | FAULT_EXP(FAULT_TS,segment & ~0x03); |
| 706 | 722 | } |
| 707 | 723 | else |
| 708 | 724 | { |
| 709 | 725 | if(segment > cpustate->gdtr.limit) |
| 710 | 726 | { |
| 711 | 727 | logerror("IRQ: Task gate: TSS is past GDT limit.\n"); |
| 712 | | FAULT_EXP(FAULT_TS,segment & ~0x07); |
| 728 | FAULT_EXP(FAULT_TS,segment & ~0x03); |
| 713 | 729 | } |
| 714 | 730 | } |
| 715 | 731 | if((desc.flags & 0x000f) != 0x09 && (desc.flags & 0x000f) != 0x01) |
| 716 | 732 | { |
| 717 | 733 | logerror("IRQ: Task gate: TSS is not an available TSS.\n"); |
| 718 | | FAULT_EXP(FAULT_TS,segment & ~0x07); |
| 734 | FAULT_EXP(FAULT_TS,segment & ~0x03); |
| 719 | 735 | } |
| 720 | 736 | if((desc.flags & 0x0080) == 0) |
| 721 | 737 | { |
| 722 | 738 | logerror("IRQ: Task gate: TSS is not present.\n"); |
| 723 | | FAULT_EXP(FAULT_NP,segment & ~0x07); |
| 739 | FAULT_EXP(FAULT_NP,segment & ~0x03); |
| 724 | 740 | } |
| 725 | 741 | if(!(irq == 3 || irq == 4 || irq == 9 || irq_gate == 1)) |
| 726 | 742 | cpustate->eip = cpustate->prev_eip; |
| r15081 | r15082 | |
| 750 | 766 | if((segment & ~0x07) > cpustate->ldtr.limit) |
| 751 | 767 | { |
| 752 | 768 | logerror("IRQ: Gate segment is past LDT limit.\n"); |
| 753 | | FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext) |
| 769 | FAULT_EXP(FAULT_GP,(segment & 0x03)+cpustate->ext) |
| 754 | 770 | } |
| 755 | 771 | } |
| 756 | 772 | else |
| r15081 | r15082 | |
| 758 | 774 | if((segment & ~0x07) > cpustate->gdtr.limit) |
| 759 | 775 | { |
| 760 | 776 | logerror("IRQ: Gate segment is past GDT limit.\n"); |
| 761 | | FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext) |
| 777 | FAULT_EXP(FAULT_GP,(segment & 0x03)+cpustate->ext) |
| 762 | 778 | } |
| 763 | 779 | } |
| 764 | 780 | if((desc.flags & 0x0018) != 0x18) |
| 765 | 781 | { |
| 766 | 782 | logerror("IRQ: Gate descriptor is not a code segment.\n"); |
| 767 | | FAULT_EXP(FAULT_GP,(segment & 0x07)+cpustate->ext) |
| 783 | FAULT_EXP(FAULT_GP,(segment & 0x03)+cpustate->ext) |
| 768 | 784 | } |
| 769 | 785 | if((desc.flags & 0x0080) == 0) |
| 770 | 786 | { |
| 771 | 787 | logerror("IRQ: Gate segment is not present.\n"); |
| 772 | | FAULT_EXP(FAULT_NP,(segment & 0x07)+cpustate->ext) |
| 788 | FAULT_EXP(FAULT_NP,(segment & 0x03)+cpustate->ext) |
| 773 | 789 | } |
| 774 | 790 | if((desc.flags & 0x0004) == 0 && (DPL < CPL)) |
| 775 | 791 | { |
| r15081 | r15082 | |
| 779 | 795 | |
| 780 | 796 | /* Check new stack segment in TSS */ |
| 781 | 797 | memset(&stack, 0, sizeof(stack)); |
| 782 | | if(flags & 0x0008) |
| 783 | | stack.selector = i386_get_stack_segment(cpustate,DPL); |
| 784 | | else |
| 785 | | stack.selector = i286_get_stack_segment(cpustate,DPL); |
| 798 | stack.selector = i386_get_stack_segment(cpustate,DPL); |
| 786 | 799 | i386_load_protected_mode_segment(cpustate,&stack,NULL); |
| 787 | 800 | oldSS = cpustate->sreg[SS].selector; |
| 788 | 801 | if(flags & 0x0008) |
| 789 | 802 | oldESP = REG32(ESP); |
| 790 | 803 | else |
| 791 | 804 | oldESP = REG16(SP); |
| 792 | | if((stack.selector & ~0x07) == 0) |
| 805 | if((stack.selector & ~0x03) == 0) |
| 793 | 806 | { |
| 794 | 807 | logerror("IRQ: New stack selector is null.\n"); |
| 795 | 808 | FAULT_EXP(FAULT_GP,cpustate->ext) |
| r15081 | r15082 | |
| 799 | 812 | if((stack.selector & ~0x07) > cpustate->ldtr.base) |
| 800 | 813 | { |
| 801 | 814 | logerror("IRQ: New stack selector is past LDT limit.\n"); |
| 802 | | FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) |
| 815 | FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext) |
| 803 | 816 | } |
| 804 | 817 | } |
| 805 | 818 | else |
| r15081 | r15082 | |
| 807 | 820 | if((stack.selector & ~0x07) > cpustate->gdtr.base) |
| 808 | 821 | { |
| 809 | 822 | logerror("IRQ: New stack selector is past GDT limit.\n"); |
| 810 | | FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) |
| 823 | FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext) |
| 811 | 824 | } |
| 812 | 825 | } |
| 813 | 826 | if((stack.selector & 0x03) != DPL) |
| 814 | 827 | { |
| 815 | 828 | logerror("IRQ: New stack selector RPL is not equal to code segment DPL.\n"); |
| 816 | | FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) |
| 829 | FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext) |
| 817 | 830 | } |
| 818 | 831 | if(((stack.flags >> 5) & 0x03) != DPL) |
| 819 | 832 | { |
| 820 | 833 | logerror("IRQ: New stack segment DPL is not equal to code segment DPL.\n"); |
| 821 | | FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) |
| 834 | FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext) |
| 822 | 835 | } |
| 823 | 836 | if(((stack.flags & 0x0018) != 0x10) && (stack.flags & 0x0002) != 0) |
| 824 | 837 | { |
| 825 | 838 | logerror("IRQ: New stack segment is not a writable data segment.\n"); |
| 826 | | FAULT_EXP(FAULT_TS,(stack.selector & ~0x07)+cpustate->ext) // #TS(stack selector + EXT) |
| 839 | FAULT_EXP(FAULT_TS,(stack.selector & ~0x03)+cpustate->ext) // #TS(stack selector + EXT) |
| 827 | 840 | } |
| 828 | 841 | if((stack.flags & 0x0080) == 0) |
| 829 | 842 | { |
| 830 | 843 | logerror("IRQ: New stack segment is not present.\n"); |
| 831 | | FAULT_EXP(FAULT_SS,(stack.selector & ~0x07)+cpustate->ext) // #TS(stack selector + EXT) |
| 844 | FAULT_EXP(FAULT_SS,(stack.selector & ~0x03)+cpustate->ext) // #TS(stack selector + EXT) |
| 832 | 845 | } |
| 846 | newESP = i386_get_stack_ptr(cpustate,DPL); |
| 833 | 847 | if(type & 0x08) // 32-bit gate |
| 834 | 848 | { |
| 835 | | newESP = i386_get_stack_ptr(cpustate,DPL); |
| 836 | 849 | if(newESP < 20) |
| 837 | 850 | { |
| 838 | 851 | logerror("IRQ: New stack has no space for return addresses.\n"); |
| r15081 | r15082 | |
| 841 | 854 | } |
| 842 | 855 | else // 16-bit gate |
| 843 | 856 | { |
| 844 | | newESP = i286_get_stack_ptr(cpustate,DPL); |
| 857 | newESP &= 0xffff; |
| 845 | 858 | if(newESP < 10) |
| 846 | 859 | { |
| 847 | 860 | logerror("IRQ: New stack has no space for return addresses.\n"); |
| r15081 | r15082 | |
| 853 | 866 | logerror("IRQ: New EIP is past code segment limit.\n"); |
| 854 | 867 | FAULT_EXP(FAULT_GP,0) |
| 855 | 868 | } |
| 869 | /* change CPL before accessing the stack */ |
| 870 | cpustate->CPL = DPL; |
| 871 | /* check for page fault at new stack TODO: check if stack frame crosses page boundary */ |
| 872 | WRITE_TEST(cpustate, stack.base+newESP-1); |
| 856 | 873 | /* Load new stack segment descriptor */ |
| 857 | 874 | cpustate->sreg[SS].selector = stack.selector; |
| 858 | 875 | i386_load_segment_descriptor(cpustate,SS); |
| 859 | 876 | if(flags & 0x0008) |
| 860 | 877 | REG32(ESP) = i386_get_stack_ptr(cpustate,DPL); |
| 861 | 878 | else |
| 862 | | REG16(SP) = i286_get_stack_ptr(cpustate,DPL); |
| 879 | REG16(SP) = i386_get_stack_ptr(cpustate,DPL); |
| 863 | 880 | if(type & 0x08) |
| 864 | 881 | { |
| 865 | 882 | // 32-bit gate |
| r15081 | r15082 | |
| 872 | 889 | PUSH16(cpustate,oldSS); |
| 873 | 890 | PUSH16(cpustate,oldESP); |
| 874 | 891 | } |
| 875 | | cpustate->CPL = DPL; |
| 876 | 892 | SetRPL = 1; |
| 877 | 893 | } |
| 878 | 894 | else |
| r15081 | r15082 | |
| 951 | 967 | { |
| 952 | 968 | UINT32 entry = irq * 8; |
| 953 | 969 | UINT32 v2,type; |
| 970 | int cpl = cpustate->CPL; |
| 971 | cpustate->CPL = 0; |
| 954 | 972 | v2 = READ32(cpustate, cpustate->idtr.base + entry + 4 ); |
| 955 | 973 | type = (v2>>8) & 0x1F; |
| 956 | 974 | if(type == 5) |
| r15081 | r15082 | |
| 959 | 977 | v2 = READ32(cpustate, cpustate->gdtr.base + ((v2 >> 16) & 0xfff8) + 4); |
| 960 | 978 | type = (v2>>8) & 0x1F; |
| 961 | 979 | } |
| 980 | cpustate->CPL = cpl; |
| 962 | 981 | if(type >= 9) |
| 963 | 982 | PUSH32(cpustate,error); |
| 964 | 983 | else |
| r15081 | r15082 | |
| 1136 | 1155 | cpustate->cr[0] |= 0x08; |
| 1137 | 1156 | |
| 1138 | 1157 | /* Load incoming task state from the new task's TSS */ |
| 1158 | cpustate->CPL = 0; |
| 1139 | 1159 | tss = cpustate->task.base; |
| 1140 | 1160 | cpustate->ldtr.segment = READ32(cpustate,tss+0x60) & 0xffff; |
| 1141 | 1161 | seg.selector = cpustate->ldtr.segment; |
| r15081 | r15082 | |
| 1351 | 1371 | } |
| 1352 | 1372 | if(call_gate.selector & 0x04) |
| 1353 | 1373 | { |
| 1354 | | /* check GDT limit */ |
| 1355 | | if((call_gate.selector & ~0x07) > cpustate->gdtr.limit) |
| 1374 | if((call_gate.selector & ~0x07) > cpustate->ldtr.limit) |
| 1356 | 1375 | { |
| 1357 | | logerror("JMP: Call Gate: Gate Selector is past GDT segment limit\n"); |
| 1376 | logerror("JMP: Call Gate: Gate Selector is past LDT segment limit\n"); |
| 1358 | 1377 | FAULT(FAULT_GP,call_gate.selector & 0xfffc) |
| 1359 | 1378 | } |
| 1360 | 1379 | } |
| 1361 | 1380 | else |
| 1362 | 1381 | { |
| 1363 | | /* check LDT limit */ |
| 1364 | | if((call_gate.selector & ~0x07) > cpustate->ldtr.limit) |
| 1382 | if((call_gate.selector & ~0x07) > cpustate->gdtr.limit) |
| 1365 | 1383 | { |
| 1366 | | logerror("JMP: Call Gate: Gate Selector is past LDT segment limit\n"); |
| 1384 | logerror("JMP: Call Gate: Gate Selector is past GDT segment limit\n"); |
| 1367 | 1385 | FAULT(FAULT_GP,call_gate.selector & 0xfffc) |
| 1368 | 1386 | } |
| 1369 | 1387 | } |
| r15081 | r15082 | |
| 1638 | 1656 | FAULT(FAULT_NP,desc.selector & ~0x03) // #GP(selector) |
| 1639 | 1657 | } |
| 1640 | 1658 | desc.selector = gate.selector; |
| 1641 | | if((gate.selector & ~0x07) == 0) |
| 1659 | if((gate.selector & ~0x03) == 0) |
| 1642 | 1660 | { |
| 1643 | 1661 | logerror("CALL: Call gate: Segment is null.\n"); |
| 1644 | 1662 | FAULT(FAULT_GP,0) // #GP(0) |
| r15081 | r15082 | |
| 1684 | 1702 | /* more privilege */ |
| 1685 | 1703 | /* Check new SS segment for privilege level from TSS */ |
| 1686 | 1704 | memset(&stack, 0, sizeof(stack)); |
| 1687 | | if(operand32 != 0) |
| 1688 | | stack.selector = i386_get_stack_segment(cpustate,DPL); |
| 1689 | | else |
| 1690 | | stack.selector = i286_get_stack_segment(cpustate,DPL); |
| 1705 | stack.selector = i386_get_stack_segment(cpustate,DPL); |
| 1691 | 1706 | i386_load_protected_mode_segment(cpustate,&stack,NULL); |
| 1692 | | if((stack.selector & ~0x07) == 0) |
| 1707 | if((stack.selector & ~0x03) == 0) |
| 1693 | 1708 | { |
| 1694 | 1709 | logerror("CALL: Call gate: TSS selector is null\n"); |
| 1695 | 1710 | FAULT(FAULT_TS,0) // #TS(0) |
| r15081 | r15082 | |
| 1730 | 1745 | logerror("CALL: Call gate: Stack segment is not present\n"); |
| 1731 | 1746 | FAULT(FAULT_SS,stack.selector) // #SS(SS selector) |
| 1732 | 1747 | } |
| 1748 | UINT32 newESP = i386_get_stack_ptr(cpustate,DPL); |
| 1733 | 1749 | if(operand32 != 0) |
| 1734 | 1750 | { |
| 1735 | | UINT32 newESP = i386_get_stack_ptr(cpustate,DPL); |
| 1736 | 1751 | if(newESP < ((gate.dword_count & 0x1f) + 16)) |
| 1737 | 1752 | { |
| 1738 | 1753 | logerror("CALL: Call gate: New stack has no room for 32-bit return address and parameters.\n"); |
| r15081 | r15082 | |
| 1746 | 1761 | } |
| 1747 | 1762 | else |
| 1748 | 1763 | { |
| 1749 | | UINT32 newESP = i286_get_stack_ptr(cpustate,DPL) & 0x0000ffff; |
| 1764 | newESP &= 0x0000ffff; |
| 1750 | 1765 | if(newESP < ((gate.dword_count & 0x1f) + 8)) |
| 1751 | 1766 | { |
| 1752 | 1767 | logerror("CALL: Call gate: New stack has no room for 16-bit return address and parameters.\n"); |
| r15081 | r15082 | |
| 1761 | 1776 | selector = gate.selector; |
| 1762 | 1777 | offset = gate.offset; |
| 1763 | 1778 | |
| 1779 | cpustate->CPL = (stack.flags >> 5) & 0x03; |
| 1780 | /* check for page fault at new stack TODO: check if stack frame crosses page boundary */ |
| 1781 | WRITE_TEST(cpustate, stack.base+newESP-1); |
| 1764 | 1782 | /* switch to new stack */ |
| 1765 | 1783 | oldSS = cpustate->sreg[SS].selector; |
| 1784 | cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,gate.selector & 0x03); |
| 1766 | 1785 | if(operand32 != 0) |
| 1767 | 1786 | { |
| 1768 | 1787 | oldESP = REG32(ESP); |
| 1769 | | cpustate->sreg[SS].selector = i386_get_stack_segment(cpustate,gate.selector & 0x03); |
| 1770 | 1788 | } |
| 1771 | 1789 | else |
| 1772 | 1790 | { |
| 1773 | 1791 | oldESP = REG16(SP); |
| 1774 | | cpustate->sreg[SS].selector = i286_get_stack_segment(cpustate,gate.selector & 0x03); |
| 1775 | 1792 | } |
| 1776 | 1793 | i386_load_segment_descriptor(cpustate, SS ); |
| 1777 | 1794 | if(operand32 != 0) |
| 1778 | 1795 | REG32(ESP) = i386_get_stack_ptr(cpustate,gate.selector & 0x03); |
| 1779 | 1796 | else |
| 1780 | | REG16(SP) = i286_get_stack_ptr(cpustate,gate.selector & 0x03) & 0x0000ffff; |
| 1797 | REG16(SP) = i386_get_stack_ptr(cpustate,gate.selector & 0x03) & 0x0000ffff; |
| 1781 | 1798 | |
| 1782 | 1799 | if(operand32 != 0) |
| 1783 | 1800 | { |
| r15081 | r15082 | |
| 1796 | 1813 | /* copy parameters from old stack to new stack */ |
| 1797 | 1814 | for(x=(gate.dword_count & 0x1f)-1;x>=0;x--) |
| 1798 | 1815 | { |
| 1799 | | UINT32 addr = temp.base + oldESP + (x*2); |
| 1800 | | PUSH16(cpustate,READ16(cpustate,addr)); |
| 1816 | UINT32 addr = oldESP + (operand32?(x*4):(x*2)); |
| 1817 | addr = temp.base + (temp.d?addr:(addr&0xffff)); |
| 1818 | if(operand32) |
| 1819 | PUSH32(cpustate,READ32(cpustate,addr)); |
| 1820 | else |
| 1821 | PUSH16(cpustate,READ16(cpustate,addr)); |
| 1801 | 1822 | } |
| 1802 | | cpustate->CPL = (stack.flags >> 5) & 0x03; |
| 1803 | 1823 | SetRPL = 1; |
| 1804 | 1824 | } |
| 1805 | 1825 | else |
| r15081 | r15082 | |
| 1842 | 1862 | if(DPL < CPL) |
| 1843 | 1863 | { |
| 1844 | 1864 | logerror("CALL: Task Gate: Gate DPL is less than CPL.\n"); |
| 1845 | | FAULT(FAULT_TS,selector & ~0x07) // #TS(selector) |
| 1865 | FAULT(FAULT_TS,selector & ~0x03) // #TS(selector) |
| 1846 | 1866 | } |
| 1847 | 1867 | if(DPL < RPL) |
| 1848 | 1868 | { |
| 1849 | 1869 | logerror("CALL: Task Gate: Gate DPL is less than RPL.\n"); |
| 1850 | | FAULT(FAULT_TS,selector & ~0x07) // #TS(selector) |
| 1870 | FAULT(FAULT_TS,selector & ~0x03) // #TS(selector) |
| 1851 | 1871 | } |
| 1852 | 1872 | if(gate.ar & 0x0080) |
| 1853 | 1873 | { |
| 1854 | 1874 | logerror("CALL: Task Gate: Gate is not present.\n"); |
| 1855 | | FAULT(FAULT_NP,selector & ~0x07) // #NP(selector) |
| 1875 | FAULT(FAULT_NP,selector & ~0x03) // #NP(selector) |
| 1856 | 1876 | } |
| 1857 | 1877 | /* Check the TSS that the task gate points to */ |
| 1858 | 1878 | desc.selector = gate.selector; |
| r15081 | r15082 | |
| 1860 | 1880 | if(gate.selector & 0x04) |
| 1861 | 1881 | { |
| 1862 | 1882 | logerror("CALL: Task Gate: TSS is not global.\n"); |
| 1863 | | FAULT(FAULT_TS,gate.selector & ~0x07) // #TS(selector) |
| 1883 | FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector) |
| 1864 | 1884 | } |
| 1865 | 1885 | else |
| 1866 | 1886 | { |
| 1867 | 1887 | if((gate.selector & ~0x07) > cpustate->gdtr.limit) |
| 1868 | 1888 | { |
| 1869 | 1889 | logerror("CALL: Task Gate: TSS is past GDT limit.\n"); |
| 1870 | | FAULT(FAULT_TS,gate.selector & ~0x07) // #TS(selector) |
| 1890 | FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector) |
| 1871 | 1891 | } |
| 1872 | 1892 | } |
| 1873 | 1893 | if(desc.flags & 0x0002) |
| 1874 | 1894 | { |
| 1875 | 1895 | logerror("CALL: Task Gate: TSS is busy.\n"); |
| 1876 | | FAULT(FAULT_TS,gate.selector & ~0x07) // #TS(selector) |
| 1896 | FAULT(FAULT_TS,gate.selector & ~0x03) // #TS(selector) |
| 1877 | 1897 | } |
| 1878 | 1898 | if(desc.flags & 0x0080) |
| 1879 | 1899 | { |
| 1880 | 1900 | logerror("CALL: Task Gate: TSS is not present.\n"); |
| 1881 | | FAULT(FAULT_NP,gate.selector & ~0x07) // #TS(selector) |
| 1901 | FAULT(FAULT_NP,gate.selector & ~0x03) // #TS(selector) |
| 1882 | 1902 | } |
| 1883 | 1903 | if(desc.flags & 0x08) |
| 1884 | 1904 | i386_task_switch(cpustate,desc.selector,1); // with nesting |
| r15081 | r15082 | |
| 1981 | 2001 | } |
| 1982 | 2002 | if(desc.flags & 0x0004) |
| 1983 | 2003 | { |
| 1984 | | if(DPL > CPL) |
| 2004 | if(DPL > RPL) |
| 1985 | 2005 | { |
| 1986 | | logerror("RETF: Conforming code segment DPL is greater than CPL.\n"); |
| 2006 | logerror("RETF: Conforming code segment DPL is greater than CS RPL.\n"); |
| 1987 | 2007 | FAULT(FAULT_GP,newCS & ~0x03) |
| 1988 | 2008 | } |
| 1989 | 2009 | } |
| 1990 | 2010 | else |
| 1991 | 2011 | { |
| 1992 | | if(DPL != CPL) |
| 2012 | if(DPL != RPL) |
| 1993 | 2013 | { |
| 1994 | | logerror("RETF: Non-conforming code segment DPL does not equal CPL.\n"); |
| 2014 | logerror("RETF: Non-conforming code segment DPL does not equal CS RPL.\n"); |
| 1995 | 2015 | FAULT(FAULT_GP,newCS & ~0x03) |
| 1996 | 2016 | } |
| 1997 | 2017 | } |
| r15081 | r15082 | |
| 2061 | 2081 | if((newCS & ~0x07) >= cpustate->ldtr.limit) |
| 2062 | 2082 | { |
| 2063 | 2083 | logerror("RETF: CS segment selector is past LDT limit.\n"); |
| 2064 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2084 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2065 | 2085 | } |
| 2066 | 2086 | } |
| 2067 | 2087 | else |
| r15081 | r15082 | |
| 2069 | 2089 | if((newCS & ~0x07) >= cpustate->gdtr.limit) |
| 2070 | 2090 | { |
| 2071 | 2091 | logerror("RETF: CS segment selector is past GDT limit.\n"); |
| 2072 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2092 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2073 | 2093 | } |
| 2074 | 2094 | } |
| 2075 | 2095 | if((desc.flags & 0x0018) != 0x0018) |
| 2076 | 2096 | { |
| 2077 | 2097 | logerror("RETF: CS segment is not a code segment.\n"); |
| 2078 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2098 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2079 | 2099 | } |
| 2080 | 2100 | if(desc.flags & 0x0004) |
| 2081 | 2101 | { |
| 2082 | 2102 | if(DPL > RPL) |
| 2083 | 2103 | { |
| 2084 | 2104 | logerror("RETF: Conforming CS segment DPL is greater than return selector RPL.\n"); |
| 2085 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2105 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2086 | 2106 | } |
| 2087 | 2107 | } |
| 2088 | 2108 | else |
| r15081 | r15082 | |
| 2090 | 2110 | if(DPL != RPL) |
| 2091 | 2111 | { |
| 2092 | 2112 | logerror("RETF: Non-conforming CS segment DPL is not equal to return selector RPL.\n"); |
| 2093 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2113 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2094 | 2114 | } |
| 2095 | 2115 | } |
| 2096 | 2116 | if((desc.flags & 0x0080) == 0) |
| 2097 | 2117 | { |
| 2098 | 2118 | logerror("RETF: CS segment is not present.\n"); |
| 2099 | | FAULT(FAULT_NP,newCS & ~0x07) |
| 2119 | FAULT(FAULT_NP,newCS & ~0x03) |
| 2100 | 2120 | } |
| 2101 | 2121 | if(newEIP > desc.limit) |
| 2102 | 2122 | { |
| r15081 | r15082 | |
| 2131 | 2151 | if((newSS & ~0x07) > cpustate->ldtr.limit) |
| 2132 | 2152 | { |
| 2133 | 2153 | logerror("RETF (%08x): SS segment selector is past LDT limit.\n",cpustate->pc); |
| 2134 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2154 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2135 | 2155 | } |
| 2136 | 2156 | } |
| 2137 | 2157 | else |
| r15081 | r15082 | |
| 2139 | 2159 | if((newSS & ~0x07) > cpustate->gdtr.limit) |
| 2140 | 2160 | { |
| 2141 | 2161 | logerror("RETF (%08x): SS segment selector is past GDT limit.\n",cpustate->pc); |
| 2142 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2162 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2143 | 2163 | } |
| 2144 | 2164 | } |
| 2145 | 2165 | if((newSS & 0x03) != RPL) |
| 2146 | 2166 | { |
| 2147 | 2167 | logerror("RETF: SS segment RPL is not equal to CS segment RPL.\n"); |
| 2148 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2168 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2149 | 2169 | } |
| 2150 | 2170 | if((desc.flags & 0x0018) != 0x0010 || (desc.flags & 0x0002) == 0) |
| 2151 | 2171 | { |
| 2152 | 2172 | logerror("RETF: SS segment is not a writable data segment.\n"); |
| 2153 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2173 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2154 | 2174 | } |
| 2155 | 2175 | if(((desc.flags >> 5) & 0x03) != RPL) |
| 2156 | 2176 | { |
| 2157 | 2177 | logerror("RETF: SS DPL is not equal to CS segment RPL.\n"); |
| 2158 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2178 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2159 | 2179 | } |
| 2160 | 2180 | if((desc.flags & 0x0080) == 0) |
| 2161 | 2181 | { |
| 2162 | 2182 | logerror("RETF: SS segment is not present.\n"); |
| 2163 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2183 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2164 | 2184 | } |
| 2165 | 2185 | cpustate->CPL = newCS & 0x03; |
| 2166 | 2186 | |
| r15081 | r15082 | |
| 2249 | 2269 | if(task & 0x04) |
| 2250 | 2270 | { |
| 2251 | 2271 | logerror("IRET: Task return: Back-linked TSS is not in GDT.\n"); |
| 2252 | | FAULT(FAULT_TS,task & ~0x07) |
| 2272 | FAULT(FAULT_TS,task & ~0x03) |
| 2253 | 2273 | } |
| 2254 | 2274 | if((task & ~0x07) >= cpustate->gdtr.limit) |
| 2255 | 2275 | { |
| 2256 | 2276 | logerror("IRET: Task return: Back-linked TSS is not in GDT.\n"); |
| 2257 | | FAULT(FAULT_TS,task & ~0x07) |
| 2277 | FAULT(FAULT_TS,task & ~0x03) |
| 2258 | 2278 | } |
| 2259 | 2279 | memset(&desc, 0, sizeof(desc)); |
| 2260 | 2280 | desc.selector = task; |
| r15081 | r15082 | |
| 2262 | 2282 | if((desc.flags & 0x001f) != 0x000b) |
| 2263 | 2283 | { |
| 2264 | 2284 | logerror("IRET (%08x): Task return: Back-linked TSS is not a busy TSS.\n",cpustate->pc); |
| 2265 | | FAULT(FAULT_TS,task & ~0x07) |
| 2285 | FAULT(FAULT_TS,task & ~0x03) |
| 2266 | 2286 | } |
| 2267 | 2287 | if((desc.flags & 0x0080) == 0) |
| 2268 | 2288 | { |
| 2269 | 2289 | logerror("IRET: Task return: Back-linked TSS is not present.\n"); |
| 2270 | | FAULT(FAULT_NP,task & ~0x07) |
| 2290 | FAULT(FAULT_NP,task & ~0x03) |
| 2271 | 2291 | } |
| 2272 | 2292 | if(desc.flags & 0x08) |
| 2273 | 2293 | i386_task_switch(cpustate,desc.selector,0); |
| r15081 | r15082 | |
| 2462 | 2482 | if(RPL < CPL) |
| 2463 | 2483 | { |
| 2464 | 2484 | logerror("IRET (%08x): Return CS RPL is less than CPL.\n",cpustate->pc); |
| 2465 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2485 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2466 | 2486 | } |
| 2467 | 2487 | if(RPL == CPL) |
| 2468 | 2488 | { |
| r15081 | r15082 | |
| 2495 | 2515 | if((newCS & ~0x07) >= cpustate->ldtr.limit) |
| 2496 | 2516 | { |
| 2497 | 2517 | logerror("IRET: Return CS selector (%04x) is past LDT limit.\n",newCS); |
| 2498 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2518 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2499 | 2519 | } |
| 2500 | 2520 | } |
| 2501 | 2521 | else |
| r15081 | r15082 | |
| 2503 | 2523 | if((newCS & ~0x07) >= cpustate->gdtr.limit) |
| 2504 | 2524 | { |
| 2505 | 2525 | logerror("IRET: Return CS selector is past GDT limit.\n"); |
| 2506 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2526 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2507 | 2527 | } |
| 2508 | 2528 | } |
| 2509 | 2529 | memset(&desc, 0, sizeof(desc)); |
| r15081 | r15082 | |
| 2518 | 2538 | } |
| 2519 | 2539 | if(desc.flags & 0x0004) |
| 2520 | 2540 | { |
| 2521 | | if(DPL > CPL) |
| 2541 | if(DPL > RPL) |
| 2522 | 2542 | { |
| 2523 | | logerror("IRET: Conforming return CS DPL is greater than CPL.\n"); |
| 2524 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2543 | logerror("IRET: Conforming return CS DPL is greater than CS RPL.\n"); |
| 2544 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2525 | 2545 | } |
| 2526 | 2546 | } |
| 2527 | 2547 | else |
| 2528 | 2548 | { |
| 2529 | | if(DPL != CPL) |
| 2549 | if(DPL != RPL) |
| 2530 | 2550 | { |
| 2531 | | logerror("IRET: Non-conforming return CS DPL is not equal to CPL.\n"); |
| 2532 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2551 | logerror("IRET: Non-conforming return CS DPL is not equal to CS RPL.\n"); |
| 2552 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2533 | 2553 | } |
| 2534 | 2554 | } |
| 2535 | 2555 | if((desc.flags & 0x0080) == 0) |
| 2536 | 2556 | { |
| 2537 | 2557 | logerror("IRET: Return CS segment is not present.\n"); |
| 2538 | | FAULT(FAULT_NP,newCS & ~0x07) |
| 2558 | FAULT(FAULT_NP,newCS & ~0x03) |
| 2539 | 2559 | } |
| 2540 | 2560 | if(newEIP > desc.limit) |
| 2541 | 2561 | { |
| r15081 | r15082 | |
| 2601 | 2621 | if((newCS & ~0x07) >= cpustate->ldtr.limit) |
| 2602 | 2622 | { |
| 2603 | 2623 | logerror("IRET: Return CS selector is past LDT limit.\n"); |
| 2604 | | FAULT(FAULT_GP,newCS & ~0x07); |
| 2624 | FAULT(FAULT_GP,newCS & ~0x03); |
| 2605 | 2625 | } |
| 2606 | 2626 | } |
| 2607 | 2627 | else |
| r15081 | r15082 | |
| 2609 | 2629 | if((newCS & ~0x07) >= cpustate->gdtr.limit) |
| 2610 | 2630 | { |
| 2611 | 2631 | logerror("IRET: Return CS selector is past GDT limit.\n"); |
| 2612 | | FAULT(FAULT_GP,newCS & ~0x07); |
| 2632 | FAULT(FAULT_GP,newCS & ~0x03); |
| 2613 | 2633 | } |
| 2614 | 2634 | } |
| 2615 | 2635 | if((desc.flags & 0x0018) != 0x0018) |
| 2616 | 2636 | { |
| 2617 | 2637 | logerror("IRET: Return CS segment is not a code segment.\n"); |
| 2618 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2638 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2619 | 2639 | } |
| 2620 | 2640 | if(desc.flags & 0x0004) |
| 2621 | 2641 | { |
| 2622 | | if(DPL <= CPL) |
| 2642 | if(DPL > RPL) |
| 2623 | 2643 | { |
| 2624 | | logerror("IRET: Conforming return CS DPL is not greater than CPL.\n"); |
| 2625 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2644 | logerror("IRET: Conforming return CS DPL is greater than CS RPL.\n"); |
| 2645 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2626 | 2646 | } |
| 2627 | 2647 | } |
| 2628 | 2648 | else |
| r15081 | r15082 | |
| 2630 | 2650 | if(DPL != RPL) |
| 2631 | 2651 | { |
| 2632 | 2652 | logerror("IRET: Non-conforming return CS DPL does not equal CS RPL.\n"); |
| 2633 | | FAULT(FAULT_GP,newCS & ~0x07) |
| 2653 | FAULT(FAULT_GP,newCS & ~0x03) |
| 2634 | 2654 | } |
| 2635 | 2655 | } |
| 2636 | 2656 | if((desc.flags & 0x0080) == 0) |
| 2637 | 2657 | { |
| 2638 | 2658 | logerror("IRET: Return CS segment is not present.\n"); |
| 2639 | | FAULT(FAULT_NP,newCS & ~0x07) |
| 2659 | FAULT(FAULT_NP,newCS & ~0x03) |
| 2640 | 2660 | } |
| 2641 | 2661 | |
| 2642 | 2662 | /* Check SS selector and descriptor */ |
| r15081 | r15082 | |
| 2664 | 2684 | if((newSS & ~0x07) >= cpustate->ldtr.limit) |
| 2665 | 2685 | { |
| 2666 | 2686 | logerror("IRET: Return SS selector is past LDT limit.\n"); |
| 2667 | | FAULT(FAULT_GP,newSS & ~0x07); |
| 2687 | FAULT(FAULT_GP,newSS & ~0x03); |
| 2668 | 2688 | } |
| 2669 | 2689 | } |
| 2670 | 2690 | else |
| r15081 | r15082 | |
| 2672 | 2692 | if((newSS & ~0x07) >= cpustate->gdtr.limit) |
| 2673 | 2693 | { |
| 2674 | 2694 | logerror("IRET: Return SS selector is past GDT limit.\n"); |
| 2675 | | FAULT(FAULT_GP,newSS & ~0x07); |
| 2695 | FAULT(FAULT_GP,newSS & ~0x03); |
| 2676 | 2696 | } |
| 2677 | 2697 | } |
| 2678 | 2698 | if((newSS & 0x03) != RPL) |
| 2679 | 2699 | { |
| 2680 | 2700 | logerror("IRET: Return SS RPL is not equal to return CS RPL.\n"); |
| 2681 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2701 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2682 | 2702 | } |
| 2683 | 2703 | if((stack.flags & 0x0018) != 0x0010) |
| 2684 | 2704 | { |
| 2685 | 2705 | logerror("IRET: Return SS segment is not a data segment.\n"); |
| 2686 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2706 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2687 | 2707 | } |
| 2688 | 2708 | if((stack.flags & 0x0002) == 0) |
| 2689 | 2709 | { |
| 2690 | 2710 | logerror("IRET: Return SS segment is not writable.\n"); |
| 2691 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2711 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2692 | 2712 | } |
| 2693 | 2713 | if(DPL != RPL) |
| 2694 | 2714 | { |
| 2695 | 2715 | logerror("IRET: Return SS DPL does not equal SS RPL.\n"); |
| 2696 | | FAULT(FAULT_GP,newSS & ~0x07) |
| 2716 | FAULT(FAULT_GP,newSS & ~0x03) |
| 2697 | 2717 | } |
| 2698 | 2718 | if((stack.flags & 0x0080) == 0) |
| 2699 | 2719 | { |
| 2700 | 2720 | logerror("IRET: Return SS segment is not present.\n"); |
| 2701 | | FAULT(FAULT_NP,newSS & ~0x07) |
| 2721 | FAULT(FAULT_NP,newSS & ~0x03) |
| 2702 | 2722 | } |
| 2703 | 2723 | if(newEIP > desc.limit) |
| 2704 | 2724 | { |
| r15081 | r15082 | |
| 3127 | 3147 | cpustate->address_prefix = 0; |
| 3128 | 3148 | |
| 3129 | 3149 | cpustate->ext = 1; |
| 3130 | | cpustate->old_tf = cpustate->TF; |
| 3150 | int old_tf = cpustate->TF; |
| 3131 | 3151 | |
| 3132 | 3152 | cpustate->segment_prefix = 0; |
| 3133 | 3153 | cpustate->prev_eip = cpustate->eip; |
| r15081 | r15082 | |
| 3142 | 3162 | try |
| 3143 | 3163 | { |
| 3144 | 3164 | I386OP(decode_opcode)(cpustate); |
| 3145 | | if(cpustate->TF && cpustate->old_tf) |
| 3165 | if(cpustate->TF && old_tf) |
| 3146 | 3166 | { |
| 3147 | 3167 | cpustate->prev_eip = cpustate->eip; |
| 3148 | 3168 | cpustate->ext = 1; |