2 * Fast486 386/486 CPU Emulation Library
5 * Copyright (C) 2015 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 /* INCLUDES *******************************************************************/
32 /* PUBLIC FUNCTIONS ***********************************************************/
36 Fast486ReadMemory(PFAST486_STATE State
,
37 FAST486_SEG_REGS SegmentReg
,
44 PFAST486_SEG_REG CachedDescriptor
;
46 ASSERT(SegmentReg
< FAST486_NUM_SEG_REGS
);
48 /* Get the cached descriptor */
49 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
51 if (InstFetch
|| CachedDescriptor
->Executable
|| !CachedDescriptor
->DirConf
)
53 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
55 /* Read beyond limit */
56 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
62 if (Offset
< CachedDescriptor
->Limit
)
64 /* Read beyond limit */
65 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
70 /* Check for protected mode */
71 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
) && !State
->Flags
.Vm
)
73 /* Privilege checks */
75 if (!CachedDescriptor
->Present
)
77 Fast486Exception(State
, FAST486_EXCEPTION_NP
);
81 if ((!InstFetch
&& (CachedDescriptor
->Rpl
> CachedDescriptor
->Dpl
))
82 || (Fast486GetCurrentPrivLevel(State
) > CachedDescriptor
->Dpl
))
84 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
90 if (!CachedDescriptor
->Executable
)
92 /* Data segment not executable */
93 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
99 if (CachedDescriptor
->Executable
&& (!CachedDescriptor
->ReadWrite
))
101 /* Code segment not readable */
102 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
108 /* Find the linear address */
109 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
111 #ifndef FAST486_NO_PREFETCH
112 if (InstFetch
&& ((Offset
+ FAST486_CACHE_SIZE
- 1) <= CachedDescriptor
->Limit
))
114 State
->PrefetchAddress
= LinearAddress
;
116 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PG
)
117 && (PAGE_OFFSET(State
->PrefetchAddress
) > (FAST486_PAGE_SIZE
- FAST486_CACHE_SIZE
)))
119 /* We mustn't prefetch across a page boundary */
120 State
->PrefetchAddress
= PAGE_ALIGN(State
->PrefetchAddress
)
121 | (FAST486_PAGE_SIZE
- FAST486_CACHE_SIZE
);
123 if ((LinearAddress
- State
->PrefetchAddress
+ Size
) >= FAST486_CACHE_SIZE
)
125 /* We can't prefetch without possibly violating page permissions */
126 State
->PrefetchValid
= FALSE
;
127 return Fast486ReadLinearMemory(State
, LinearAddress
, Buffer
, Size
, TRUE
);
132 if (Fast486ReadLinearMemory(State
,
133 State
->PrefetchAddress
,
134 State
->PrefetchCache
,
138 State
->PrefetchValid
= TRUE
;
140 RtlMoveMemory(Buffer
,
141 &State
->PrefetchCache
[LinearAddress
- State
->PrefetchAddress
],
147 State
->PrefetchValid
= FALSE
;
154 /* Read from the linear address */
155 return Fast486ReadLinearMemory(State
, LinearAddress
, Buffer
, Size
, TRUE
);
161 Fast486WriteMemory(PFAST486_STATE State
,
162 FAST486_SEG_REGS SegmentReg
,
168 PFAST486_SEG_REG CachedDescriptor
;
170 ASSERT(SegmentReg
< FAST486_NUM_SEG_REGS
);
172 /* Get the cached descriptor */
173 CachedDescriptor
= &State
->SegmentRegs
[SegmentReg
];
175 if (CachedDescriptor
->Executable
|| !CachedDescriptor
->DirConf
)
177 if ((Offset
+ Size
- 1) > CachedDescriptor
->Limit
)
179 /* Write beyond limit */
180 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
186 if (Offset
< CachedDescriptor
->Limit
)
188 /* Read beyond limit */
189 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
194 /* Check for protected mode */
195 if ((State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
) && !State
->Flags
.Vm
)
197 /* Privilege checks */
199 if (!CachedDescriptor
->Present
)
201 Fast486Exception(State
, FAST486_EXCEPTION_NP
);
205 if ((CachedDescriptor
->Rpl
> CachedDescriptor
->Dpl
)
206 || (Fast486GetCurrentPrivLevel(State
) > CachedDescriptor
->Dpl
))
208 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
212 if (CachedDescriptor
->Executable
)
214 /* Code segment not writable */
215 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
218 else if (!CachedDescriptor
->ReadWrite
)
220 /* Data segment not writeable */
221 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
226 /* Find the linear address */
227 LinearAddress
= CachedDescriptor
->Base
+ Offset
;
229 #ifndef FAST486_NO_PREFETCH
230 if (State
->PrefetchValid
231 && (LinearAddress
>= State
->PrefetchAddress
)
232 && ((LinearAddress
+ Size
) <= (State
->PrefetchAddress
+ FAST486_CACHE_SIZE
)))
234 /* Update the prefetch */
235 RtlMoveMemory(&State
->PrefetchCache
[LinearAddress
- State
->PrefetchAddress
],
237 min(Size
, FAST486_CACHE_SIZE
+ State
->PrefetchAddress
- LinearAddress
));
241 /* Write to the linear address */
242 return Fast486WriteLinearMemory(State
, LinearAddress
, Buffer
, Size
, TRUE
);
245 static inline BOOLEAN
247 Fast486GetIntVector(PFAST486_STATE State
,
249 PFAST486_IDT_ENTRY IdtEntry
)
251 /* Check for protected mode */
252 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
254 /* Read from the IDT */
255 if (!Fast486ReadLinearMemory(State
,
257 + Number
* sizeof(*IdtEntry
),
262 /* Exception occurred */
268 /* Read from the real-mode IVT */
271 /* Paging is always disabled in real mode */
272 State
->MemReadCallback(State
,
274 + Number
* sizeof(FarPointer
),
278 /* Fill a fake IDT entry */
279 IdtEntry
->Offset
= LOWORD(FarPointer
);
280 IdtEntry
->Selector
= HIWORD(FarPointer
);
282 IdtEntry
->Type
= FAST486_IDT_INT_GATE
;
283 IdtEntry
->Storage
= FALSE
;
285 IdtEntry
->Present
= TRUE
;
286 IdtEntry
->OffsetHigh
= 0;
292 static inline BOOLEAN
294 Fast486InterruptInternal(PFAST486_STATE State
,
295 PFAST486_IDT_ENTRY IdtEntry
,
296 BOOLEAN PushErrorCode
,
299 BOOLEAN GateSize
= (IdtEntry
->Type
== FAST486_IDT_INT_GATE_32
) ||
300 (IdtEntry
->Type
== FAST486_IDT_TRAP_GATE_32
);
301 USHORT OldCs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
302 ULONG OldEip
= State
->InstPtr
.Long
;
303 ULONG OldFlags
= State
->Flags
.Long
;
304 UCHAR OldCpl
= State
->Cpl
;
306 /* Check for protected mode */
307 if (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
)
309 USHORT OldSs
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
310 ULONG OldEsp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
311 BOOLEAN OldVm
= State
->Flags
.Vm
;
313 if (IdtEntry
->Type
== FAST486_TASK_GATE_SIGNATURE
)
316 return Fast486TaskSwitch(State
, FAST486_TASK_CALL
, IdtEntry
->Selector
);
319 /* Check if the interrupt handler is more privileged or if we're in V86 mode */
320 if ((OldCpl
> GET_SEGMENT_RPL(IdtEntry
->Selector
)) || State
->Flags
.Vm
)
323 PFAST486_LEGACY_TSS LegacyTss
= (PFAST486_LEGACY_TSS
)&Tss
;
328 if (!Fast486ReadLinearMemory(State
,
331 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
332 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
335 /* Exception occurred */
339 /* Switch to the new privilege level */
340 State
->Cpl
= GET_SEGMENT_RPL(IdtEntry
->Selector
);
342 /* Check the new (higher) privilege level */
347 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
354 NewSs
= LegacyTss
->Ss0
;
355 NewEsp
= LegacyTss
->Sp0
;
363 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
370 NewSs
= LegacyTss
->Ss1
;
371 NewEsp
= LegacyTss
->Sp1
;
379 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
386 NewSs
= LegacyTss
->Ss2
;
387 NewEsp
= LegacyTss
->Sp2
;
395 /* Should never reach here! */
400 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, NewSs
))
402 /* Exception occurred */
406 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewEsp
;
410 /* Clear the VM flag */
411 State
->Flags
.Vm
= FALSE
;
416 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, IdtEntry
->Selector
))
418 /* An exception occurred during the jump */
424 /* 32-bit code segment, use EIP */
425 State
->InstPtr
.Long
= MAKELONG(IdtEntry
->Offset
, IdtEntry
->OffsetHigh
);
429 /* 16-bit code segment, use IP */
430 State
->InstPtr
.LowWord
= IdtEntry
->Offset
;
433 /* Clear TF and NT */
434 State
->Flags
.Tf
= State
->Flags
.Nt
= FALSE
;
438 /* Push GS, FS, DS and ES */
439 if (!Fast486StackPushInternal(State
,
441 State
->SegmentRegs
[FAST486_REG_GS
].Selector
))
445 if (!Fast486StackPushInternal(State
,
447 State
->SegmentRegs
[FAST486_REG_FS
].Selector
))
451 if (!Fast486StackPushInternal(State
,
453 State
->SegmentRegs
[FAST486_REG_DS
].Selector
))
457 if (!Fast486StackPushInternal(State
,
459 State
->SegmentRegs
[FAST486_REG_ES
].Selector
))
464 /* Now load them with NULL selectors, since they are useless in protected mode */
465 if (!Fast486LoadSegment(State
, FAST486_REG_GS
, 0)) return FALSE
;
466 if (!Fast486LoadSegment(State
, FAST486_REG_FS
, 0)) return FALSE
;
467 if (!Fast486LoadSegment(State
, FAST486_REG_DS
, 0)) return FALSE
;
468 if (!Fast486LoadSegment(State
, FAST486_REG_ES
, 0)) return FALSE
;
471 /* Check if the interrupt handler is more privileged or we're in VM86 mode (again) */
472 if ((OldCpl
> GET_SEGMENT_RPL(IdtEntry
->Selector
)) || OldVm
)
474 /* Push SS selector */
475 if (!Fast486StackPushInternal(State
, GateSize
, OldSs
)) return FALSE
;
477 /* Push the stack pointer */
478 if (!Fast486StackPushInternal(State
, GateSize
, OldEsp
)) return FALSE
;
484 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, IdtEntry
->Selector
))
486 /* An exception occurred during the jump */
491 State
->InstPtr
.LowWord
= IdtEntry
->Offset
;
495 if (!Fast486StackPushInternal(State
, GateSize
, OldFlags
)) return FALSE
;
497 /* Push CS selector */
498 if (!Fast486StackPushInternal(State
, GateSize
, OldCs
)) return FALSE
;
500 /* Push the instruction pointer */
501 if (!Fast486StackPushInternal(State
, GateSize
, OldEip
)) return FALSE
;
505 /* Push the error code */
506 if (!Fast486StackPushInternal(State
, GateSize
, ErrorCode
)) return FALSE
;
509 if ((IdtEntry
->Type
== FAST486_IDT_INT_GATE
)
510 || (IdtEntry
->Type
== FAST486_IDT_INT_GATE_32
))
512 /* Disable interrupts after a jump to an interrupt gate handler */
513 State
->Flags
.If
= FALSE
;
521 Fast486PerformInterrupt(PFAST486_STATE State
,
524 FAST486_IDT_ENTRY IdtEntry
;
526 /* Get the interrupt vector */
527 if (!Fast486GetIntVector(State
, Number
, &IdtEntry
))
529 /* Exception occurred */
533 /* Perform the interrupt */
534 if (!Fast486InterruptInternal(State
, &IdtEntry
, FALSE
, 0))
536 /* Exception occurred */
545 Fast486ExceptionWithErrorCode(PFAST486_STATE State
,
546 FAST486_EXCEPTIONS ExceptionCode
,
549 FAST486_IDT_ENTRY IdtEntry
;
551 /* Increment the exception count */
552 State
->ExceptionCount
++;
554 /* Check if the exception occurred more than once */
555 if (State
->ExceptionCount
> 1)
557 /* Then this is a double fault */
558 ExceptionCode
= FAST486_EXCEPTION_DF
;
561 /* Check if this is a triple fault */
562 if (State
->ExceptionCount
== 3)
564 DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
565 State
->SegmentRegs
[FAST486_REG_CS
].Selector
,
566 State
->InstPtr
.Long
);
573 /* Clear the prefix flags */
574 State
->PrefixFlags
= 0;
576 /* Restore the IP to the saved IP */
577 State
->InstPtr
= State
->SavedInstPtr
;
579 /* Get the interrupt vector */
580 if (!Fast486GetIntVector(State
, ExceptionCode
, &IdtEntry
))
583 * If this function failed, that means Fast486Exception
584 * was called again, so just return in this case.
589 /* Perform the interrupt */
590 if (!Fast486InterruptInternal(State
,
592 EXCEPTION_HAS_ERROR_CODE(ExceptionCode
)
593 && (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
),
597 * If this function failed, that means Fast486Exception
598 * was called again, so just return in this case.
603 /* Reset the exception count */
604 State
->ExceptionCount
= 0;
609 Fast486TaskSwitch(PFAST486_STATE State
, FAST486_TASK_SWITCH_TYPE Type
, USHORT Selector
)
613 FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor
;
615 PFAST486_LEGACY_TSS OldLegacyTss
= (PFAST486_LEGACY_TSS
)&OldTss
;
617 PFAST486_LEGACY_TSS NewLegacyTss
= (PFAST486_LEGACY_TSS
)&NewTss
;
618 USHORT NewLdtr
, NewEs
, NewCs
, NewSs
, NewDs
;
620 if (State
->TaskReg
.Limit
< (sizeof(FAST486_TSS
) - 1)
621 && State
->TaskReg
.Limit
!= (sizeof(FAST486_LEGACY_TSS
) - 1))
623 /* Invalid task register limit */
624 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, State
->TaskReg
.Selector
);
628 /* Read the old TSS */
629 if (!Fast486ReadLinearMemory(State
,
632 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
633 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
636 /* Exception occurred */
641 /* If this is a task return, use the linked previous selector */
642 if (Type
== FAST486_TASK_RETURN
)
644 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)) Selector
= LOWORD(OldTss
.Link
);
645 else Selector
= OldLegacyTss
->Link
;
648 /* Make sure the entry exists in the GDT (not LDT!) */
649 if ((GET_SEGMENT_INDEX(Selector
) == 0)
650 || (Selector
& SEGMENT_TABLE_INDICATOR
)
651 || GET_SEGMENT_INDEX(Selector
) >= (State
->Gdtr
.Size
+ 1u))
653 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, Selector
);
657 /* Get the TSS descriptor from the GDT */
658 if (!Fast486ReadLinearMemory(State
,
659 State
->Gdtr
.Address
+ GET_SEGMENT_INDEX(Selector
),
661 sizeof(NewTssDescriptor
),
664 /* Exception occurred */
668 if (!NewTssDescriptor
.Present
)
670 /* Incoming task TSS not present */
671 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_NP
, Selector
);
675 /* Calculate the linear address of the new TSS */
676 NewTssAddress
= NewTssDescriptor
.Base
;
677 NewTssAddress
|= NewTssDescriptor
.BaseMid
<< 16;
678 NewTssAddress
|= NewTssDescriptor
.BaseHigh
<< 24;
680 /* Calculate the limit of the new TSS */
681 NewTssLimit
= NewTssDescriptor
.Limit
| (NewTssDescriptor
.LimitHigh
<< 16);
683 if (NewTssDescriptor
.Granularity
)
686 NewTssLimit
|= 0x00000FFF;
689 if (NewTssLimit
< (sizeof(FAST486_TSS
) - 1)
690 && NewTssLimit
!= (sizeof(FAST486_LEGACY_TSS
) - 1))
692 /* TSS limit invalid */
693 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, Selector
);
698 * The incoming task shouldn't be busy if we're executing it as a
699 * new task, and it should be busy if we're returning to it.
701 if ((((NewTssDescriptor
.Signature
!= FAST486_TSS_SIGNATURE
)
702 && (NewTssDescriptor
.Signature
!= FAST486_TSS_16_SIGNATURE
))
703 || (Type
== FAST486_TASK_RETURN
))
704 && (((NewTssDescriptor
.Signature
!= FAST486_BUSY_TSS_SIGNATURE
)
705 && (NewTssDescriptor
.Signature
!= FAST486_BUSY_TSS_16_SIGNATURE
))
706 || (Type
!= FAST486_TASK_RETURN
)))
708 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Selector
);
712 /* Read the new TSS */
713 if (!Fast486ReadLinearMemory(State
,
716 NewTssLimit
>= (sizeof(FAST486_TSS
) - 1)
717 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
720 /* Exception occurred */
724 if (Type
!= FAST486_TASK_CALL
)
726 /* Clear the busy bit of the outgoing task */
727 FAST486_SYSTEM_DESCRIPTOR OldTssDescriptor
;
729 if (!Fast486ReadLinearMemory(State
,
731 + GET_SEGMENT_INDEX(State
->TaskReg
.Selector
),
733 sizeof(OldTssDescriptor
),
736 /* Exception occurred */
740 OldTssDescriptor
.Signature
= FAST486_TSS_SIGNATURE
;
742 if (!Fast486WriteLinearMemory(State
,
744 + GET_SEGMENT_INDEX(State
->TaskReg
.Selector
),
746 sizeof(OldTssDescriptor
),
749 /* Exception occurred */
756 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1)) NewTss
.Link
= State
->TaskReg
.Selector
;
757 else NewLegacyTss
->Link
= State
->TaskReg
.Selector
;
760 /* Save the current task into the TSS */
761 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
763 OldTss
.Cr3
= State
->ControlRegisters
[FAST486_REG_CR3
];
764 OldTss
.Eip
= State
->InstPtr
.Long
;
765 OldTss
.Eflags
= State
->Flags
.Long
;
766 OldTss
.Eax
= State
->GeneralRegs
[FAST486_REG_EAX
].Long
;
767 OldTss
.Ecx
= State
->GeneralRegs
[FAST486_REG_ECX
].Long
;
768 OldTss
.Edx
= State
->GeneralRegs
[FAST486_REG_EDX
].Long
;
769 OldTss
.Ebx
= State
->GeneralRegs
[FAST486_REG_EBX
].Long
;
770 OldTss
.Esp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
771 OldTss
.Ebp
= State
->GeneralRegs
[FAST486_REG_EBP
].Long
;
772 OldTss
.Esi
= State
->GeneralRegs
[FAST486_REG_ESI
].Long
;
773 OldTss
.Edi
= State
->GeneralRegs
[FAST486_REG_EDI
].Long
;
774 OldTss
.Es
= State
->SegmentRegs
[FAST486_REG_ES
].Selector
;
775 OldTss
.Cs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
776 OldTss
.Ss
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
777 OldTss
.Ds
= State
->SegmentRegs
[FAST486_REG_DS
].Selector
;
778 OldTss
.Fs
= State
->SegmentRegs
[FAST486_REG_FS
].Selector
;
779 OldTss
.Gs
= State
->SegmentRegs
[FAST486_REG_GS
].Selector
;
780 OldTss
.Ldtr
= State
->Ldtr
.Selector
;
784 OldLegacyTss
->Ip
= State
->InstPtr
.LowWord
;
785 OldLegacyTss
->Flags
= State
->Flags
.LowWord
;
786 OldLegacyTss
->Ax
= State
->GeneralRegs
[FAST486_REG_EAX
].LowWord
;
787 OldLegacyTss
->Cx
= State
->GeneralRegs
[FAST486_REG_ECX
].LowWord
;
788 OldLegacyTss
->Dx
= State
->GeneralRegs
[FAST486_REG_EDX
].LowWord
;
789 OldLegacyTss
->Bx
= State
->GeneralRegs
[FAST486_REG_EBX
].LowWord
;
790 OldLegacyTss
->Sp
= State
->GeneralRegs
[FAST486_REG_ESP
].LowWord
;
791 OldLegacyTss
->Bp
= State
->GeneralRegs
[FAST486_REG_EBP
].LowWord
;
792 OldLegacyTss
->Si
= State
->GeneralRegs
[FAST486_REG_ESI
].LowWord
;
793 OldLegacyTss
->Di
= State
->GeneralRegs
[FAST486_REG_EDI
].LowWord
;
794 OldLegacyTss
->Es
= State
->SegmentRegs
[FAST486_REG_ES
].Selector
;
795 OldLegacyTss
->Cs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
796 OldLegacyTss
->Ss
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
797 OldLegacyTss
->Ds
= State
->SegmentRegs
[FAST486_REG_DS
].Selector
;
798 OldLegacyTss
->Ldtr
= State
->Ldtr
.Selector
;
801 /* Write back the old TSS */
802 if (!Fast486WriteLinearMemory(State
,
805 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
806 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
809 /* Exception occurred */
813 /* Mark the new task as busy */
814 NewTssDescriptor
.Signature
= FAST486_BUSY_TSS_SIGNATURE
;
816 /* Write back the new TSS descriptor */
817 if (!Fast486WriteLinearMemory(State
,
818 State
->Gdtr
.Address
+ GET_SEGMENT_INDEX(Selector
),
820 sizeof(NewTssDescriptor
),
823 /* Exception occurred */
827 /* Set the task switch bit */
828 State
->ControlRegisters
[FAST486_REG_CR0
] |= FAST486_CR0_TS
;
830 /* Load the task register with the new values */
831 State
->TaskReg
.Selector
= Selector
;
832 State
->TaskReg
.Base
= NewTssAddress
;
833 State
->TaskReg
.Limit
= NewTssLimit
;
835 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1))
837 /* Change the page directory */
838 State
->ControlRegisters
[FAST486_REG_CR3
] = NewTss
.Cr3
;
842 Fast486FlushTlb(State
);
845 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1)) State
->Cpl
= GET_SEGMENT_RPL(NewTss
.Cs
);
846 else State
->Cpl
= GET_SEGMENT_RPL(NewLegacyTss
->Cs
);
848 #ifndef FAST486_NO_PREFETCH
849 /* Context switching invalidates the prefetch */
850 State
->PrefetchValid
= FALSE
;
853 /* Load the registers */
854 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1))
856 State
->InstPtr
.Long
= State
->SavedInstPtr
.Long
= NewTss
.Eip
;
857 State
->Flags
.Long
= NewTss
.Eflags
;
858 State
->GeneralRegs
[FAST486_REG_EAX
].Long
= NewTss
.Eax
;
859 State
->GeneralRegs
[FAST486_REG_ECX
].Long
= NewTss
.Ecx
;
860 State
->GeneralRegs
[FAST486_REG_EDX
].Long
= NewTss
.Edx
;
861 State
->GeneralRegs
[FAST486_REG_EBX
].Long
= NewTss
.Ebx
;
862 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewTss
.Esp
;
863 State
->GeneralRegs
[FAST486_REG_EBP
].Long
= NewTss
.Ebp
;
864 State
->GeneralRegs
[FAST486_REG_ESI
].Long
= NewTss
.Esi
;
865 State
->GeneralRegs
[FAST486_REG_EDI
].Long
= NewTss
.Edi
;
870 NewLdtr
= NewTss
.Ldtr
;
874 State
->InstPtr
.LowWord
= State
->SavedInstPtr
.LowWord
= NewLegacyTss
->Ip
;
875 State
->Flags
.LowWord
= NewLegacyTss
->Flags
;
876 State
->GeneralRegs
[FAST486_REG_EAX
].LowWord
= NewLegacyTss
->Ax
;
877 State
->GeneralRegs
[FAST486_REG_ECX
].LowWord
= NewLegacyTss
->Cx
;
878 State
->GeneralRegs
[FAST486_REG_EDX
].LowWord
= NewLegacyTss
->Dx
;
879 State
->GeneralRegs
[FAST486_REG_EBX
].LowWord
= NewLegacyTss
->Bx
;
880 State
->GeneralRegs
[FAST486_REG_ESP
].LowWord
= NewLegacyTss
->Sp
;
881 State
->GeneralRegs
[FAST486_REG_EBP
].LowWord
= NewLegacyTss
->Bp
;
882 State
->GeneralRegs
[FAST486_REG_ESI
].LowWord
= NewLegacyTss
->Si
;
883 State
->GeneralRegs
[FAST486_REG_EDI
].LowWord
= NewLegacyTss
->Di
;
884 NewEs
= NewLegacyTss
->Es
;
885 NewCs
= NewLegacyTss
->Cs
;
886 NewSs
= NewLegacyTss
->Ss
;
887 NewDs
= NewLegacyTss
->Ds
;
888 NewLdtr
= NewLegacyTss
->Ldtr
;
891 /* Set the NT flag if nesting */
892 if (Type
== FAST486_TASK_CALL
) State
->Flags
.Nt
= TRUE
;
894 if (GET_SEGMENT_INDEX(NewLdtr
) != 0)
897 FAST486_SYSTEM_DESCRIPTOR GdtEntry
;
899 if (NewLdtr
& SEGMENT_TABLE_INDICATOR
)
901 /* This selector doesn't point to the GDT */
902 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
906 if (!Fast486ReadDescriptorEntry(State
, NewLdtr
, &Valid
, (PFAST486_GDT_ENTRY
)&GdtEntry
))
908 /* Exception occurred */
914 /* Invalid selector */
915 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
919 if (GdtEntry
.Signature
!= FAST486_LDT_SIGNATURE
)
921 /* This is not an LDT descriptor */
922 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
926 if (!GdtEntry
.Present
)
928 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
932 /* Update the LDTR */
933 State
->Ldtr
.Selector
= NewLdtr
;
934 State
->Ldtr
.Base
= GdtEntry
.Base
| (GdtEntry
.BaseMid
<< 16) | (GdtEntry
.BaseHigh
<< 24);
935 State
->Ldtr
.Limit
= GdtEntry
.Limit
| (GdtEntry
.LimitHigh
<< 16);
937 if (GdtEntry
.Granularity
)
939 State
->Ldtr
.Limit
<<= 12;
940 State
->Ldtr
.Limit
|= 0x00000FFF;
945 /* The LDT of this task is empty */
946 RtlZeroMemory(&State
->Ldtr
, sizeof(State
->Ldtr
));
949 /* Load the new segments */
950 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_CS
, NewCs
, FAST486_EXCEPTION_TS
))
955 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_SS
, NewSs
, FAST486_EXCEPTION_TS
))
960 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_ES
, NewEs
, FAST486_EXCEPTION_TS
))
965 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_DS
, NewDs
, FAST486_EXCEPTION_TS
))
970 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1))
972 if (!Fast486LoadSegmentInternal(State
,
975 FAST486_EXCEPTION_TS
))
980 if (!Fast486LoadSegmentInternal(State
,
983 FAST486_EXCEPTION_TS
))
994 Fast486CallGate(PFAST486_STATE State
,
995 PFAST486_CALL_GATE Gate
,
999 FAST486_GDT_ENTRY NewCodeSegment
;
1000 BOOLEAN GateSize
= (Gate
->Type
== FAST486_CALL_GATE_SIGNATURE
);
1002 PFAST486_LEGACY_TSS LegacyTss
= (PFAST486_LEGACY_TSS
)&Tss
;
1003 USHORT OldCs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
1004 ULONG OldEip
= State
->InstPtr
.Long
;
1005 USHORT OldCpl
= State
->Cpl
;
1006 USHORT OldSs
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
1007 ULONG OldEsp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
1008 ULONG ParamBuffer
[32]; /* Maximum possible size - 32 DWORDs */
1009 PULONG LongParams
= (PULONG
)ParamBuffer
;
1010 PUSHORT ShortParams
= (PUSHORT
)ParamBuffer
;
1012 if (!Gate
->Selector
)
1014 /* The code segment is NULL */
1015 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
1019 if (!Fast486ReadDescriptorEntry(State
, Gate
->Selector
, &Valid
, &NewCodeSegment
))
1021 /* Exception occurred */
1025 if (!Valid
|| (NewCodeSegment
.Dpl
> Fast486GetCurrentPrivLevel(State
)))
1027 /* Code segment invalid */
1028 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Gate
->Selector
);
1032 if (Call
&& Gate
->ParamCount
)
1034 /* Read the parameters */
1035 if (!Fast486ReadMemory(State
,
1040 Gate
->ParamCount
* sizeof(ULONG
)))
1042 /* Exception occurred */
1047 /* Check if the new code segment is more privileged */
1048 if (NewCodeSegment
.Dpl
< OldCpl
)
1056 if (!Fast486ReadLinearMemory(State
,
1057 State
->TaskReg
.Base
,
1059 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
1060 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
1063 /* Exception occurred */
1067 /* Switch to the new privilege level */
1068 State
->Cpl
= NewCodeSegment
.Dpl
;
1070 /* Check the new (higher) privilege level */
1075 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
1082 NewSs
= LegacyTss
->Ss0
;
1083 NewEsp
= LegacyTss
->Sp0
;
1091 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
1098 NewSs
= LegacyTss
->Ss1
;
1099 NewEsp
= LegacyTss
->Sp1
;
1107 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
1114 NewSs
= LegacyTss
->Ss2
;
1115 NewEsp
= LegacyTss
->Sp2
;
1123 /* Should never reach here! */
1128 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, NewSs
))
1130 /* Exception occurred */
1134 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewEsp
;
1136 else if (!NewCodeSegment
.DirConf
)
1138 /* This is not allowed for jumps */
1139 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Gate
->Selector
);
1145 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, Gate
->Selector
))
1147 /* An exception occurred during the jump */
1151 /* Set the instruction pointer */
1152 if (GateSize
) State
->InstPtr
.Long
= MAKELONG(Gate
->Offset
, Gate
->OffsetHigh
);
1153 else State
->InstPtr
.Long
= Gate
->Offset
;
1159 /* Check if the new code segment is more privileged (again) */
1160 if (NewCodeSegment
.Dpl
< OldCpl
)
1162 /* Push SS selector */
1163 if (!Fast486StackPushInternal(State
, GateSize
, OldSs
)) return FALSE
;
1165 /* Push stack pointer */
1166 if (!Fast486StackPushInternal(State
, GateSize
, OldEsp
)) return FALSE
;
1169 /* Push the parameters in reverse order */
1170 for (i
= Gate
->ParamCount
- 1; i
>= 0; i
--)
1172 if (!Fast486StackPushInternal(State
,
1174 GateSize
? LongParams
[i
] : ShortParams
[i
]))
1176 /* Exception occurred */
1181 /* Push CS selector */
1182 if (!Fast486StackPushInternal(State
, GateSize
, OldCs
)) return FALSE
;
1184 /* Push the instruction pointer */
1185 if (!Fast486StackPushInternal(State
, GateSize
, OldEip
)) return FALSE
;