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
;
434 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
;
517 State
->Flags
.Tf
= FALSE
;
524 Fast486PerformInterrupt(PFAST486_STATE State
,
527 FAST486_IDT_ENTRY IdtEntry
;
529 /* Get the interrupt vector */
530 if (!Fast486GetIntVector(State
, Number
, &IdtEntry
))
532 /* Exception occurred */
536 /* Perform the interrupt */
537 if (!Fast486InterruptInternal(State
, &IdtEntry
, FALSE
, 0))
539 /* Exception occurred */
548 Fast486ExceptionWithErrorCode(PFAST486_STATE State
,
549 FAST486_EXCEPTIONS ExceptionCode
,
552 FAST486_IDT_ENTRY IdtEntry
;
554 /* Increment the exception count */
555 State
->ExceptionCount
++;
557 /* Check if the exception occurred more than once */
558 if (State
->ExceptionCount
> 1)
560 /* Then this is a double fault */
561 ExceptionCode
= FAST486_EXCEPTION_DF
;
564 /* Check if this is a triple fault */
565 if (State
->ExceptionCount
== 3)
567 DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
568 State
->SegmentRegs
[FAST486_REG_CS
].Selector
,
569 State
->InstPtr
.Long
);
576 /* Clear the prefix flags */
577 State
->PrefixFlags
= 0;
579 /* Restore the IP to the saved IP */
580 State
->InstPtr
= State
->SavedInstPtr
;
582 /* Get the interrupt vector */
583 if (!Fast486GetIntVector(State
, ExceptionCode
, &IdtEntry
))
586 * If this function failed, that means Fast486Exception
587 * was called again, so just return in this case.
592 /* Perform the interrupt */
593 if (!Fast486InterruptInternal(State
,
595 EXCEPTION_HAS_ERROR_CODE(ExceptionCode
)
596 && (State
->ControlRegisters
[FAST486_REG_CR0
] & FAST486_CR0_PE
),
600 * If this function failed, that means Fast486Exception
601 * was called again, so just return in this case.
606 /* Reset the exception count */
607 State
->ExceptionCount
= 0;
612 Fast486TaskSwitch(PFAST486_STATE State
, FAST486_TASK_SWITCH_TYPE Type
, USHORT Selector
)
616 FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor
;
618 PFAST486_LEGACY_TSS OldLegacyTss
= (PFAST486_LEGACY_TSS
)&OldTss
;
620 PFAST486_LEGACY_TSS NewLegacyTss
= (PFAST486_LEGACY_TSS
)&NewTss
;
621 USHORT NewLdtr
, NewEs
, NewCs
, NewSs
, NewDs
;
623 if (State
->TaskReg
.Limit
< (sizeof(FAST486_TSS
) - 1)
624 && State
->TaskReg
.Limit
!= (sizeof(FAST486_LEGACY_TSS
) - 1))
626 /* Invalid task register limit */
627 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, State
->TaskReg
.Selector
);
631 /* Read the old TSS */
632 if (!Fast486ReadLinearMemory(State
,
635 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
636 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
639 /* Exception occurred */
644 /* If this is a task return, use the linked previous selector */
645 if (Type
== FAST486_TASK_RETURN
)
647 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)) Selector
= LOWORD(OldTss
.Link
);
648 else Selector
= OldLegacyTss
->Link
;
651 /* Make sure the entry exists in the GDT (not LDT!) */
652 if ((GET_SEGMENT_INDEX(Selector
) == 0)
653 || (Selector
& SEGMENT_TABLE_INDICATOR
)
654 || GET_SEGMENT_INDEX(Selector
) >= (State
->Gdtr
.Size
+ 1u))
656 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, Selector
);
660 /* Get the TSS descriptor from the GDT */
661 if (!Fast486ReadLinearMemory(State
,
662 State
->Gdtr
.Address
+ GET_SEGMENT_INDEX(Selector
),
664 sizeof(NewTssDescriptor
),
667 /* Exception occurred */
671 if (!NewTssDescriptor
.Present
)
673 /* Incoming task TSS not present */
674 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_NP
, Selector
);
678 /* Calculate the linear address of the new TSS */
679 NewTssAddress
= NewTssDescriptor
.Base
;
680 NewTssAddress
|= NewTssDescriptor
.BaseMid
<< 16;
681 NewTssAddress
|= NewTssDescriptor
.BaseHigh
<< 24;
683 /* Calculate the limit of the new TSS */
684 NewTssLimit
= NewTssDescriptor
.Limit
| (NewTssDescriptor
.LimitHigh
<< 16);
686 if (NewTssDescriptor
.Granularity
)
689 NewTssLimit
|= 0x00000FFF;
692 if (NewTssLimit
< (sizeof(FAST486_TSS
) - 1)
693 && NewTssLimit
!= (sizeof(FAST486_LEGACY_TSS
) - 1))
695 /* TSS limit invalid */
696 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, Selector
);
701 * The incoming task shouldn't be busy if we're executing it as a
702 * new task, and it should be busy if we're returning to it.
704 if ((((NewTssDescriptor
.Signature
!= FAST486_TSS_SIGNATURE
)
705 && (NewTssDescriptor
.Signature
!= FAST486_TSS_16_SIGNATURE
))
706 || (Type
== FAST486_TASK_RETURN
))
707 && (((NewTssDescriptor
.Signature
!= FAST486_BUSY_TSS_SIGNATURE
)
708 && (NewTssDescriptor
.Signature
!= FAST486_BUSY_TSS_16_SIGNATURE
))
709 || (Type
!= FAST486_TASK_RETURN
)))
711 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Selector
);
715 /* Read the new TSS */
716 if (!Fast486ReadLinearMemory(State
,
719 NewTssLimit
>= (sizeof(FAST486_TSS
) - 1)
720 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
723 /* Exception occurred */
727 if (Type
!= FAST486_TASK_CALL
)
729 /* Clear the busy bit of the outgoing task */
730 FAST486_SYSTEM_DESCRIPTOR OldTssDescriptor
;
732 if (!Fast486ReadLinearMemory(State
,
734 + GET_SEGMENT_INDEX(State
->TaskReg
.Selector
),
736 sizeof(OldTssDescriptor
),
739 /* Exception occurred */
743 OldTssDescriptor
.Signature
= FAST486_TSS_SIGNATURE
;
745 if (!Fast486WriteLinearMemory(State
,
747 + GET_SEGMENT_INDEX(State
->TaskReg
.Selector
),
749 sizeof(OldTssDescriptor
),
752 /* Exception occurred */
759 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1)) NewTss
.Link
= State
->TaskReg
.Selector
;
760 else NewLegacyTss
->Link
= State
->TaskReg
.Selector
;
763 /* Save the current task into the TSS */
764 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
766 OldTss
.Cr3
= State
->ControlRegisters
[FAST486_REG_CR3
];
767 OldTss
.Eip
= State
->InstPtr
.Long
;
768 OldTss
.Eflags
= State
->Flags
.Long
;
769 OldTss
.Eax
= State
->GeneralRegs
[FAST486_REG_EAX
].Long
;
770 OldTss
.Ecx
= State
->GeneralRegs
[FAST486_REG_ECX
].Long
;
771 OldTss
.Edx
= State
->GeneralRegs
[FAST486_REG_EDX
].Long
;
772 OldTss
.Ebx
= State
->GeneralRegs
[FAST486_REG_EBX
].Long
;
773 OldTss
.Esp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
774 OldTss
.Ebp
= State
->GeneralRegs
[FAST486_REG_EBP
].Long
;
775 OldTss
.Esi
= State
->GeneralRegs
[FAST486_REG_ESI
].Long
;
776 OldTss
.Edi
= State
->GeneralRegs
[FAST486_REG_EDI
].Long
;
777 OldTss
.Es
= State
->SegmentRegs
[FAST486_REG_ES
].Selector
;
778 OldTss
.Cs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
779 OldTss
.Ss
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
780 OldTss
.Ds
= State
->SegmentRegs
[FAST486_REG_DS
].Selector
;
781 OldTss
.Fs
= State
->SegmentRegs
[FAST486_REG_FS
].Selector
;
782 OldTss
.Gs
= State
->SegmentRegs
[FAST486_REG_GS
].Selector
;
783 OldTss
.Ldtr
= State
->Ldtr
.Selector
;
787 OldLegacyTss
->Ip
= State
->InstPtr
.LowWord
;
788 OldLegacyTss
->Flags
= State
->Flags
.LowWord
;
789 OldLegacyTss
->Ax
= State
->GeneralRegs
[FAST486_REG_EAX
].LowWord
;
790 OldLegacyTss
->Cx
= State
->GeneralRegs
[FAST486_REG_ECX
].LowWord
;
791 OldLegacyTss
->Dx
= State
->GeneralRegs
[FAST486_REG_EDX
].LowWord
;
792 OldLegacyTss
->Bx
= State
->GeneralRegs
[FAST486_REG_EBX
].LowWord
;
793 OldLegacyTss
->Sp
= State
->GeneralRegs
[FAST486_REG_ESP
].LowWord
;
794 OldLegacyTss
->Bp
= State
->GeneralRegs
[FAST486_REG_EBP
].LowWord
;
795 OldLegacyTss
->Si
= State
->GeneralRegs
[FAST486_REG_ESI
].LowWord
;
796 OldLegacyTss
->Di
= State
->GeneralRegs
[FAST486_REG_EDI
].LowWord
;
797 OldLegacyTss
->Es
= State
->SegmentRegs
[FAST486_REG_ES
].Selector
;
798 OldLegacyTss
->Cs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
799 OldLegacyTss
->Ss
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
800 OldLegacyTss
->Ds
= State
->SegmentRegs
[FAST486_REG_DS
].Selector
;
801 OldLegacyTss
->Ldtr
= State
->Ldtr
.Selector
;
804 /* Write back the old TSS */
805 if (!Fast486WriteLinearMemory(State
,
808 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
809 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
812 /* Exception occurred */
816 /* Mark the new task as busy */
817 NewTssDescriptor
.Signature
= FAST486_BUSY_TSS_SIGNATURE
;
819 /* Write back the new TSS descriptor */
820 if (!Fast486WriteLinearMemory(State
,
821 State
->Gdtr
.Address
+ GET_SEGMENT_INDEX(Selector
),
823 sizeof(NewTssDescriptor
),
826 /* Exception occurred */
830 /* Set the task switch bit */
831 State
->ControlRegisters
[FAST486_REG_CR0
] |= FAST486_CR0_TS
;
833 /* Load the task register with the new values */
834 State
->TaskReg
.Selector
= Selector
;
835 State
->TaskReg
.Base
= NewTssAddress
;
836 State
->TaskReg
.Limit
= NewTssLimit
;
838 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1))
840 /* Change the page directory */
841 State
->ControlRegisters
[FAST486_REG_CR3
] = NewTss
.Cr3
;
845 Fast486FlushTlb(State
);
848 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1)) State
->Cpl
= GET_SEGMENT_RPL(NewTss
.Cs
);
849 else State
->Cpl
= GET_SEGMENT_RPL(NewLegacyTss
->Cs
);
851 #ifndef FAST486_NO_PREFETCH
852 /* Context switching invalidates the prefetch */
853 State
->PrefetchValid
= FALSE
;
856 /* Load the registers */
857 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1))
859 State
->InstPtr
.Long
= State
->SavedInstPtr
.Long
= NewTss
.Eip
;
860 State
->Flags
.Long
= NewTss
.Eflags
;
861 State
->GeneralRegs
[FAST486_REG_EAX
].Long
= NewTss
.Eax
;
862 State
->GeneralRegs
[FAST486_REG_ECX
].Long
= NewTss
.Ecx
;
863 State
->GeneralRegs
[FAST486_REG_EDX
].Long
= NewTss
.Edx
;
864 State
->GeneralRegs
[FAST486_REG_EBX
].Long
= NewTss
.Ebx
;
865 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewTss
.Esp
;
866 State
->GeneralRegs
[FAST486_REG_EBP
].Long
= NewTss
.Ebp
;
867 State
->GeneralRegs
[FAST486_REG_ESI
].Long
= NewTss
.Esi
;
868 State
->GeneralRegs
[FAST486_REG_EDI
].Long
= NewTss
.Edi
;
873 NewLdtr
= NewTss
.Ldtr
;
877 State
->InstPtr
.LowWord
= State
->SavedInstPtr
.LowWord
= NewLegacyTss
->Ip
;
878 State
->Flags
.LowWord
= NewLegacyTss
->Flags
;
879 State
->GeneralRegs
[FAST486_REG_EAX
].LowWord
= NewLegacyTss
->Ax
;
880 State
->GeneralRegs
[FAST486_REG_ECX
].LowWord
= NewLegacyTss
->Cx
;
881 State
->GeneralRegs
[FAST486_REG_EDX
].LowWord
= NewLegacyTss
->Dx
;
882 State
->GeneralRegs
[FAST486_REG_EBX
].LowWord
= NewLegacyTss
->Bx
;
883 State
->GeneralRegs
[FAST486_REG_ESP
].LowWord
= NewLegacyTss
->Sp
;
884 State
->GeneralRegs
[FAST486_REG_EBP
].LowWord
= NewLegacyTss
->Bp
;
885 State
->GeneralRegs
[FAST486_REG_ESI
].LowWord
= NewLegacyTss
->Si
;
886 State
->GeneralRegs
[FAST486_REG_EDI
].LowWord
= NewLegacyTss
->Di
;
887 NewEs
= NewLegacyTss
->Es
;
888 NewCs
= NewLegacyTss
->Cs
;
889 NewSs
= NewLegacyTss
->Ss
;
890 NewDs
= NewLegacyTss
->Ds
;
891 NewLdtr
= NewLegacyTss
->Ldtr
;
894 /* Set the NT flag if nesting */
895 if (Type
== FAST486_TASK_CALL
) State
->Flags
.Nt
= TRUE
;
897 if (GET_SEGMENT_INDEX(NewLdtr
) != 0)
900 FAST486_SYSTEM_DESCRIPTOR GdtEntry
;
902 if (NewLdtr
& SEGMENT_TABLE_INDICATOR
)
904 /* This selector doesn't point to the GDT */
905 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
909 if (!Fast486ReadDescriptorEntry(State
, NewLdtr
, &Valid
, (PFAST486_GDT_ENTRY
)&GdtEntry
))
911 /* Exception occurred */
917 /* Invalid selector */
918 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
922 if (GdtEntry
.Signature
!= FAST486_LDT_SIGNATURE
)
924 /* This is not an LDT descriptor */
925 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
929 if (!GdtEntry
.Present
)
931 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_TS
, NewLdtr
);
935 /* Update the LDTR */
936 State
->Ldtr
.Selector
= NewLdtr
;
937 State
->Ldtr
.Base
= GdtEntry
.Base
| (GdtEntry
.BaseMid
<< 16) | (GdtEntry
.BaseHigh
<< 24);
938 State
->Ldtr
.Limit
= GdtEntry
.Limit
| (GdtEntry
.LimitHigh
<< 16);
940 if (GdtEntry
.Granularity
)
942 State
->Ldtr
.Limit
<<= 12;
943 State
->Ldtr
.Limit
|= 0x00000FFF;
948 /* The LDT of this task is empty */
949 RtlZeroMemory(&State
->Ldtr
, sizeof(State
->Ldtr
));
952 /* Load the new segments */
953 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_CS
, NewCs
, FAST486_EXCEPTION_TS
))
958 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_SS
, NewSs
, FAST486_EXCEPTION_TS
))
963 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_ES
, NewEs
, FAST486_EXCEPTION_TS
))
968 if (!Fast486LoadSegmentInternal(State
, FAST486_REG_DS
, NewDs
, FAST486_EXCEPTION_TS
))
973 if (NewTssLimit
>= (sizeof(FAST486_TSS
) - 1))
975 if (!Fast486LoadSegmentInternal(State
,
978 FAST486_EXCEPTION_TS
))
983 if (!Fast486LoadSegmentInternal(State
,
986 FAST486_EXCEPTION_TS
))
997 Fast486CallGate(PFAST486_STATE State
,
998 PFAST486_CALL_GATE Gate
,
1002 FAST486_GDT_ENTRY NewCodeSegment
;
1003 BOOLEAN GateSize
= (Gate
->Type
== FAST486_CALL_GATE_SIGNATURE
);
1005 PFAST486_LEGACY_TSS LegacyTss
= (PFAST486_LEGACY_TSS
)&Tss
;
1006 USHORT OldCs
= State
->SegmentRegs
[FAST486_REG_CS
].Selector
;
1007 ULONG OldEip
= State
->InstPtr
.Long
;
1008 USHORT OldCpl
= State
->Cpl
;
1009 USHORT OldSs
= State
->SegmentRegs
[FAST486_REG_SS
].Selector
;
1010 ULONG OldEsp
= State
->GeneralRegs
[FAST486_REG_ESP
].Long
;
1011 ULONG ParamBuffer
[32]; /* Maximum possible size - 32 DWORDs */
1012 PULONG LongParams
= (PULONG
)ParamBuffer
;
1013 PUSHORT ShortParams
= (PUSHORT
)ParamBuffer
;
1015 if (!Gate
->Selector
)
1017 /* The code segment is NULL */
1018 Fast486Exception(State
, FAST486_EXCEPTION_GP
);
1022 if (!Fast486ReadDescriptorEntry(State
, Gate
->Selector
, &Valid
, &NewCodeSegment
))
1024 /* Exception occurred */
1028 if (!Valid
|| (NewCodeSegment
.Dpl
> Fast486GetCurrentPrivLevel(State
)))
1030 /* Code segment invalid */
1031 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Gate
->Selector
);
1035 if (Call
&& Gate
->ParamCount
)
1037 /* Read the parameters */
1038 if (!Fast486ReadMemory(State
,
1043 Gate
->ParamCount
* sizeof(ULONG
)))
1045 /* Exception occurred */
1050 /* Check if the new code segment is more privileged */
1051 if (NewCodeSegment
.Dpl
< OldCpl
)
1059 if (!Fast486ReadLinearMemory(State
,
1060 State
->TaskReg
.Base
,
1062 State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1)
1063 ? sizeof(FAST486_TSS
) : sizeof(FAST486_LEGACY_TSS
),
1066 /* Exception occurred */
1070 /* Switch to the new privilege level */
1071 State
->Cpl
= NewCodeSegment
.Dpl
;
1073 /* Check the new (higher) privilege level */
1078 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
1085 NewSs
= LegacyTss
->Ss0
;
1086 NewEsp
= LegacyTss
->Sp0
;
1094 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
1101 NewSs
= LegacyTss
->Ss1
;
1102 NewEsp
= LegacyTss
->Sp1
;
1110 if (State
->TaskReg
.Limit
>= (sizeof(FAST486_TSS
) - 1))
1117 NewSs
= LegacyTss
->Ss2
;
1118 NewEsp
= LegacyTss
->Sp2
;
1126 /* Should never reach here! */
1131 if (!Fast486LoadSegment(State
, FAST486_REG_SS
, NewSs
))
1133 /* Exception occurred */
1137 State
->GeneralRegs
[FAST486_REG_ESP
].Long
= NewEsp
;
1139 else if (!NewCodeSegment
.DirConf
)
1141 /* This is not allowed for jumps */
1142 Fast486ExceptionWithErrorCode(State
, FAST486_EXCEPTION_GP
, Gate
->Selector
);
1148 if (!Fast486LoadSegment(State
, FAST486_REG_CS
, Gate
->Selector
))
1150 /* An exception occurred during the jump */
1154 /* Set the instruction pointer */
1155 if (GateSize
) State
->InstPtr
.Long
= MAKELONG(Gate
->Offset
, Gate
->OffsetHigh
);
1156 else State
->InstPtr
.Long
= Gate
->Offset
;
1162 /* Check if the new code segment is more privileged (again) */
1163 if (NewCodeSegment
.Dpl
< OldCpl
)
1165 /* Push SS selector */
1166 if (!Fast486StackPushInternal(State
, GateSize
, OldSs
)) return FALSE
;
1168 /* Push stack pointer */
1169 if (!Fast486StackPushInternal(State
, GateSize
, OldEsp
)) return FALSE
;
1172 /* Push the parameters in reverse order */
1173 for (i
= Gate
->ParamCount
- 1; i
>= 0; i
--)
1175 if (!Fast486StackPushInternal(State
,
1177 GateSize
? LongParams
[i
] : ShortParams
[i
]))
1179 /* Exception occurred */
1184 /* Push CS selector */
1185 if (!Fast486StackPushInternal(State
, GateSize
, OldCs
)) return FALSE
;
1187 /* Push the instruction pointer */
1188 if (!Fast486StackPushInternal(State
, GateSize
, OldEip
)) return FALSE
;