mirror of
https://github.com/reactos/reactos.git
synced 2024-08-16 16:39:08 +00:00
1314 lines
41 KiB
C
1314 lines
41 KiB
C
/*
|
|
* Fast486 386/486 CPU Emulation Library
|
|
* common.c
|
|
*
|
|
* Copyright (C) 2015 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <windef.h>
|
|
|
|
// #define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#include <fast486.h>
|
|
#include "common.h"
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
Fast486ReadMemory(PFAST486_STATE State,
|
|
FAST486_SEG_REGS SegmentReg,
|
|
ULONG Offset,
|
|
BOOLEAN InstFetch,
|
|
PVOID Buffer,
|
|
ULONG Size)
|
|
{
|
|
ULONG LinearAddress;
|
|
PFAST486_SEG_REG CachedDescriptor;
|
|
FAST486_EXCEPTIONS Exception = SegmentReg != FAST486_REG_SS
|
|
? FAST486_EXCEPTION_GP : FAST486_EXCEPTION_SS;
|
|
|
|
ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
|
|
|
|
/* Get the cached descriptor */
|
|
CachedDescriptor = &State->SegmentRegs[SegmentReg];
|
|
|
|
if (InstFetch || CachedDescriptor->Executable || !CachedDescriptor->DirConf)
|
|
{
|
|
if ((Offset + Size - 1) > CachedDescriptor->Limit)
|
|
{
|
|
/* Read beyond limit */
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Offset < CachedDescriptor->Limit)
|
|
{
|
|
/* Read beyond limit */
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Check for protected mode */
|
|
if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
|
|
{
|
|
/* Privilege checks */
|
|
|
|
if (!CachedDescriptor->Present)
|
|
{
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((!InstFetch && (CachedDescriptor->Rpl > CachedDescriptor->Dpl))
|
|
|| (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl))
|
|
{
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
|
|
if (InstFetch)
|
|
{
|
|
if (!CachedDescriptor->Executable)
|
|
{
|
|
/* Data segment not executable */
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (CachedDescriptor->Executable && (!CachedDescriptor->ReadWrite))
|
|
{
|
|
/* Code segment not readable */
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Find the linear address */
|
|
LinearAddress = CachedDescriptor->Base + Offset;
|
|
|
|
#ifndef FAST486_NO_PREFETCH
|
|
if (InstFetch && ((Offset + FAST486_CACHE_SIZE - 1) <= CachedDescriptor->Limit))
|
|
{
|
|
State->PrefetchAddress = LinearAddress;
|
|
|
|
if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PG)
|
|
&& (PAGE_OFFSET(State->PrefetchAddress) > (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE)))
|
|
{
|
|
/* We mustn't prefetch across a page boundary */
|
|
State->PrefetchAddress = PAGE_ALIGN(State->PrefetchAddress)
|
|
| (FAST486_PAGE_SIZE - FAST486_CACHE_SIZE);
|
|
|
|
if ((LinearAddress - State->PrefetchAddress + Size) >= FAST486_CACHE_SIZE)
|
|
{
|
|
/* We can't prefetch without possibly violating page permissions */
|
|
State->PrefetchValid = FALSE;
|
|
return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size, TRUE);
|
|
}
|
|
}
|
|
|
|
/* Prefetch */
|
|
if (Fast486ReadLinearMemory(State,
|
|
State->PrefetchAddress,
|
|
State->PrefetchCache,
|
|
FAST486_CACHE_SIZE,
|
|
TRUE))
|
|
{
|
|
State->PrefetchValid = TRUE;
|
|
|
|
RtlMoveMemory(Buffer,
|
|
&State->PrefetchCache[LinearAddress - State->PrefetchAddress],
|
|
Size);
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
State->PrefetchValid = FALSE;
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
/* Read from the linear address */
|
|
return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size, TRUE);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
Fast486WriteMemory(PFAST486_STATE State,
|
|
FAST486_SEG_REGS SegmentReg,
|
|
ULONG Offset,
|
|
PVOID Buffer,
|
|
ULONG Size)
|
|
{
|
|
ULONG LinearAddress;
|
|
PFAST486_SEG_REG CachedDescriptor;
|
|
FAST486_EXCEPTIONS Exception = SegmentReg != FAST486_REG_SS
|
|
? FAST486_EXCEPTION_GP : FAST486_EXCEPTION_SS;
|
|
|
|
ASSERT(SegmentReg < FAST486_NUM_SEG_REGS);
|
|
|
|
/* Get the cached descriptor */
|
|
CachedDescriptor = &State->SegmentRegs[SegmentReg];
|
|
|
|
if (CachedDescriptor->Executable || !CachedDescriptor->DirConf)
|
|
{
|
|
if ((Offset + Size - 1) > CachedDescriptor->Limit)
|
|
{
|
|
/* Write beyond limit */
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (Offset < CachedDescriptor->Limit)
|
|
{
|
|
/* Write beyond limit */
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Check for protected mode */
|
|
if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
|
|
{
|
|
/* Privilege checks */
|
|
|
|
if (!CachedDescriptor->Present)
|
|
{
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((CachedDescriptor->Rpl > CachedDescriptor->Dpl)
|
|
|| (Fast486GetCurrentPrivLevel(State) > CachedDescriptor->Dpl))
|
|
{
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
|
|
if (CachedDescriptor->Executable)
|
|
{
|
|
/* Code segment not writable */
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
else if (!CachedDescriptor->ReadWrite)
|
|
{
|
|
/* Data segment not writeable */
|
|
Fast486Exception(State, Exception);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Find the linear address */
|
|
LinearAddress = CachedDescriptor->Base + Offset;
|
|
|
|
#ifndef FAST486_NO_PREFETCH
|
|
if (State->PrefetchValid
|
|
&& (LinearAddress >= State->PrefetchAddress)
|
|
&& ((LinearAddress + Size) <= (State->PrefetchAddress + FAST486_CACHE_SIZE)))
|
|
{
|
|
/* Update the prefetch */
|
|
RtlMoveMemory(&State->PrefetchCache[LinearAddress - State->PrefetchAddress],
|
|
Buffer,
|
|
min(Size, FAST486_CACHE_SIZE + State->PrefetchAddress - LinearAddress));
|
|
}
|
|
#endif
|
|
|
|
/* Write to the linear address */
|
|
return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size, TRUE);
|
|
}
|
|
|
|
static inline BOOLEAN
|
|
FASTCALL
|
|
Fast486GetIntVector(PFAST486_STATE State,
|
|
UCHAR Number,
|
|
PFAST486_IDT_ENTRY IdtEntry)
|
|
{
|
|
/* Check for protected mode */
|
|
if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
|
|
{
|
|
/* Read from the IDT */
|
|
if (!Fast486ReadLinearMemory(State,
|
|
State->Idtr.Address
|
|
+ Number * sizeof(*IdtEntry),
|
|
IdtEntry,
|
|
sizeof(*IdtEntry),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Read from the real-mode IVT */
|
|
ULONG FarPointer;
|
|
|
|
/* Paging is always disabled in real mode */
|
|
State->MemReadCallback(State,
|
|
State->Idtr.Address
|
|
+ Number * sizeof(FarPointer),
|
|
&FarPointer,
|
|
sizeof(FarPointer));
|
|
|
|
/* Fill a fake IDT entry */
|
|
IdtEntry->Offset = LOWORD(FarPointer);
|
|
IdtEntry->Selector = HIWORD(FarPointer);
|
|
IdtEntry->Zero = 0;
|
|
IdtEntry->Type = FAST486_IDT_INT_GATE;
|
|
IdtEntry->Storage = FALSE;
|
|
IdtEntry->Dpl = 0;
|
|
IdtEntry->Present = TRUE;
|
|
IdtEntry->OffsetHigh = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static inline BOOLEAN
|
|
FASTCALL
|
|
Fast486InterruptInternal(PFAST486_STATE State,
|
|
PFAST486_IDT_ENTRY IdtEntry,
|
|
BOOLEAN PushErrorCode,
|
|
ULONG ErrorCode)
|
|
{
|
|
BOOLEAN GateSize = (IdtEntry->Type == FAST486_IDT_INT_GATE_32) ||
|
|
(IdtEntry->Type == FAST486_IDT_TRAP_GATE_32);
|
|
USHORT OldCs = State->SegmentRegs[FAST486_REG_CS].Selector;
|
|
ULONG OldEip = State->InstPtr.Long;
|
|
ULONG OldFlags = State->Flags.Long;
|
|
UCHAR OldCpl = State->Cpl;
|
|
|
|
/* Check for protected mode */
|
|
if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
|
|
{
|
|
USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
|
|
ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
|
|
BOOLEAN OldVm = State->Flags.Vm;
|
|
|
|
if (IdtEntry->Type == FAST486_TASK_GATE_SIGNATURE)
|
|
{
|
|
/* Task call */
|
|
if (!Fast486TaskSwitch(State, FAST486_TASK_CALL, IdtEntry->Selector))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
goto Finish;
|
|
}
|
|
|
|
/* Check if the interrupt handler is more privileged or if we're in V86 mode */
|
|
if ((OldCpl > GET_SEGMENT_RPL(IdtEntry->Selector)) || State->Flags.Vm)
|
|
{
|
|
FAST486_TSS Tss;
|
|
PFAST486_LEGACY_TSS LegacyTss = (PFAST486_LEGACY_TSS)&Tss;
|
|
USHORT NewSs;
|
|
ULONG NewEsp;
|
|
|
|
/* Read the TSS */
|
|
if (!Fast486ReadLinearMemory(State,
|
|
State->TaskReg.Base,
|
|
&Tss,
|
|
State->TaskReg.Modern
|
|
? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Switch to the new privilege level */
|
|
State->Cpl = GET_SEGMENT_RPL(IdtEntry->Selector);
|
|
|
|
/* Clear the VM flag */
|
|
State->Flags.Vm = FALSE;
|
|
|
|
/* Check the new (higher) privilege level */
|
|
switch (State->Cpl)
|
|
{
|
|
case 0:
|
|
{
|
|
if (State->TaskReg.Modern)
|
|
{
|
|
NewSs = Tss.Ss0;
|
|
NewEsp = Tss.Esp0;
|
|
}
|
|
else
|
|
{
|
|
NewSs = LegacyTss->Ss0;
|
|
NewEsp = LegacyTss->Sp0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
if (State->TaskReg.Modern)
|
|
{
|
|
NewSs = Tss.Ss1;
|
|
NewEsp = Tss.Esp1;
|
|
}
|
|
else
|
|
{
|
|
NewSs = LegacyTss->Ss1;
|
|
NewEsp = LegacyTss->Sp1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
if (State->TaskReg.Modern)
|
|
{
|
|
NewSs = Tss.Ss2;
|
|
NewEsp = Tss.Esp2;
|
|
}
|
|
else
|
|
{
|
|
NewSs = LegacyTss->Ss2;
|
|
NewEsp = LegacyTss->Sp2;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
/* Should never reach here! */
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
if (!Fast486LoadSegment(State, FAST486_REG_SS, NewSs))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewEsp;
|
|
}
|
|
|
|
/* Load new CS */
|
|
if (!Fast486LoadSegment(State, FAST486_REG_CS, IdtEntry->Selector))
|
|
{
|
|
/* An exception occurred during the jump */
|
|
return FALSE;
|
|
}
|
|
|
|
if (GateSize)
|
|
{
|
|
/* 32-bit code segment, use EIP */
|
|
State->InstPtr.Long = MAKELONG(IdtEntry->Offset, IdtEntry->OffsetHigh);
|
|
}
|
|
else
|
|
{
|
|
/* 16-bit code segment, use IP */
|
|
State->InstPtr.LowWord = IdtEntry->Offset;
|
|
}
|
|
|
|
/* Clear NT */
|
|
State->Flags.Nt = FALSE;
|
|
|
|
if (OldVm)
|
|
{
|
|
/* Push GS, FS, DS and ES */
|
|
if (!Fast486StackPushInternal(State,
|
|
GateSize,
|
|
State->SegmentRegs[FAST486_REG_GS].Selector))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (!Fast486StackPushInternal(State,
|
|
GateSize,
|
|
State->SegmentRegs[FAST486_REG_FS].Selector))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (!Fast486StackPushInternal(State,
|
|
GateSize,
|
|
State->SegmentRegs[FAST486_REG_DS].Selector))
|
|
{
|
|
return FALSE;
|
|
}
|
|
if (!Fast486StackPushInternal(State,
|
|
GateSize,
|
|
State->SegmentRegs[FAST486_REG_ES].Selector))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
/* Now load them with NULL selectors, since they are useless in protected mode */
|
|
if (!Fast486LoadSegment(State, FAST486_REG_GS, 0)) return FALSE;
|
|
if (!Fast486LoadSegment(State, FAST486_REG_FS, 0)) return FALSE;
|
|
if (!Fast486LoadSegment(State, FAST486_REG_DS, 0)) return FALSE;
|
|
if (!Fast486LoadSegment(State, FAST486_REG_ES, 0)) return FALSE;
|
|
}
|
|
|
|
/* Check if the interrupt handler is more privileged or we're in VM86 mode (again) */
|
|
if ((OldCpl > GET_SEGMENT_RPL(IdtEntry->Selector)) || OldVm)
|
|
{
|
|
/* Push SS selector */
|
|
if (!Fast486StackPushInternal(State, GateSize, OldSs)) return FALSE;
|
|
|
|
/* Push the stack pointer */
|
|
if (!Fast486StackPushInternal(State, GateSize, OldEsp)) return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Load new CS */
|
|
if (!Fast486LoadSegment(State, FAST486_REG_CS, IdtEntry->Selector))
|
|
{
|
|
/* An exception occurred during the jump */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set the new IP */
|
|
State->InstPtr.LowWord = IdtEntry->Offset;
|
|
}
|
|
|
|
/* Push EFLAGS */
|
|
if (!Fast486StackPushInternal(State, GateSize, OldFlags)) return FALSE;
|
|
|
|
/* Push CS selector */
|
|
if (!Fast486StackPushInternal(State, GateSize, OldCs)) return FALSE;
|
|
|
|
/* Push the instruction pointer */
|
|
if (!Fast486StackPushInternal(State, GateSize, OldEip)) return FALSE;
|
|
|
|
Finish:
|
|
|
|
if (PushErrorCode)
|
|
{
|
|
/* Push the error code */
|
|
if (!Fast486StackPushInternal(State, GateSize, ErrorCode)) return FALSE;
|
|
}
|
|
|
|
if ((IdtEntry->Type == FAST486_IDT_INT_GATE)
|
|
|| (IdtEntry->Type == FAST486_IDT_INT_GATE_32))
|
|
{
|
|
/* Disable interrupts after a jump to an interrupt gate handler */
|
|
State->Flags.If = FALSE;
|
|
}
|
|
|
|
/* Clear TF */
|
|
State->Flags.Tf = FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
Fast486PerformInterrupt(PFAST486_STATE State,
|
|
UCHAR Number)
|
|
{
|
|
FAST486_IDT_ENTRY IdtEntry;
|
|
|
|
/* Get the interrupt vector */
|
|
if (!Fast486GetIntVector(State, Number, &IdtEntry))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Perform the interrupt */
|
|
if (!Fast486InterruptInternal(State, &IdtEntry, FALSE, 0))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
Fast486ExceptionWithErrorCode(PFAST486_STATE State,
|
|
FAST486_EXCEPTIONS ExceptionCode,
|
|
ULONG ErrorCode)
|
|
{
|
|
FAST486_IDT_ENTRY IdtEntry;
|
|
|
|
/* Increment the exception count */
|
|
State->ExceptionCount++;
|
|
|
|
/* Check if the exception occurred more than once */
|
|
if (State->ExceptionCount > 1)
|
|
{
|
|
/* Then this is a double fault */
|
|
ExceptionCode = FAST486_EXCEPTION_DF;
|
|
}
|
|
|
|
/* Check if this is a triple fault */
|
|
if (State->ExceptionCount == 3)
|
|
{
|
|
DPRINT("Fast486ExceptionWithErrorCode(%04X:%08X) -- Triple fault\n",
|
|
State->SegmentRegs[FAST486_REG_CS].Selector,
|
|
State->InstPtr.Long);
|
|
|
|
/* Reset the CPU */
|
|
Fast486Reset(State);
|
|
return;
|
|
}
|
|
|
|
/* Clear the prefix flags */
|
|
State->PrefixFlags = 0;
|
|
|
|
/* Restore the IP to the saved IP */
|
|
State->InstPtr = State->SavedInstPtr;
|
|
|
|
/* Restore the SP to the saved SP */
|
|
State->GeneralRegs[FAST486_REG_ESP] = State->SavedStackPtr;
|
|
|
|
/* Get the interrupt vector */
|
|
if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry))
|
|
{
|
|
/*
|
|
* If this function failed, that means Fast486Exception
|
|
* was called again, so just return in this case.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/* Perform the interrupt */
|
|
if (!Fast486InterruptInternal(State,
|
|
&IdtEntry,
|
|
EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
|
|
&& (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE),
|
|
ErrorCode))
|
|
{
|
|
/*
|
|
* If this function failed, that means Fast486Exception
|
|
* was called again, so just return in this case.
|
|
*/
|
|
return;
|
|
}
|
|
|
|
/* Reset the exception count */
|
|
State->ExceptionCount = 0;
|
|
}
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Selector)
|
|
{
|
|
ULONG NewTssAddress;
|
|
ULONG NewTssLimit;
|
|
FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor;
|
|
FAST486_TSS OldTss;
|
|
PFAST486_LEGACY_TSS OldLegacyTss = (PFAST486_LEGACY_TSS)&OldTss;
|
|
FAST486_TSS NewTss;
|
|
PFAST486_LEGACY_TSS NewLegacyTss = (PFAST486_LEGACY_TSS)&NewTss;
|
|
USHORT NewLdtr, NewEs, NewCs, NewSs, NewDs;
|
|
|
|
if ((State->TaskReg.Modern && State->TaskReg.Limit < (sizeof(FAST486_TSS) - 1))
|
|
|| (!State->TaskReg.Modern && State->TaskReg.Limit < (sizeof(FAST486_LEGACY_TSS) - 1)))
|
|
{
|
|
/* Invalid task register limit */
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, State->TaskReg.Selector);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Read the old TSS */
|
|
if (!Fast486ReadLinearMemory(State,
|
|
State->TaskReg.Base,
|
|
&OldTss,
|
|
State->TaskReg.Modern
|
|
? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* If this is a task return, use the linked previous selector */
|
|
if (Type == FAST486_TASK_RETURN)
|
|
{
|
|
if (State->TaskReg.Modern) Selector = LOWORD(OldTss.Link);
|
|
else Selector = OldLegacyTss->Link;
|
|
}
|
|
|
|
/* Make sure the entry exists in the GDT (not LDT!) */
|
|
if ((GET_SEGMENT_INDEX(Selector) == 0)
|
|
|| (Selector & SEGMENT_TABLE_INDICATOR)
|
|
|| GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
|
|
{
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Get the TSS descriptor from the GDT */
|
|
if (!Fast486ReadLinearMemory(State,
|
|
State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
|
|
&NewTssDescriptor,
|
|
sizeof(NewTssDescriptor),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
if (!NewTssDescriptor.Present)
|
|
{
|
|
/* Incoming task TSS not present */
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_NP, Selector);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Calculate the linear address of the new TSS */
|
|
NewTssAddress = NewTssDescriptor.Base;
|
|
NewTssAddress |= NewTssDescriptor.BaseMid << 16;
|
|
NewTssAddress |= NewTssDescriptor.BaseHigh << 24;
|
|
|
|
/* Calculate the limit of the new TSS */
|
|
NewTssLimit = NewTssDescriptor.Limit | (NewTssDescriptor.LimitHigh << 16);
|
|
|
|
if (NewTssDescriptor.Granularity)
|
|
{
|
|
NewTssLimit <<= 12;
|
|
NewTssLimit |= 0x00000FFF;
|
|
}
|
|
|
|
if (NewTssLimit < (sizeof(FAST486_TSS) - 1)
|
|
&& NewTssLimit != (sizeof(FAST486_LEGACY_TSS) - 1))
|
|
{
|
|
/* TSS limit invalid */
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* The incoming task shouldn't be busy if we're executing it as a
|
|
* new task, and it should be busy if we're returning to it.
|
|
*/
|
|
if ((((NewTssDescriptor.Signature != FAST486_TSS_SIGNATURE)
|
|
&& (NewTssDescriptor.Signature != FAST486_TSS_16_SIGNATURE))
|
|
|| (Type == FAST486_TASK_RETURN))
|
|
&& (((NewTssDescriptor.Signature != FAST486_BUSY_TSS_SIGNATURE)
|
|
&& (NewTssDescriptor.Signature != FAST486_BUSY_TSS_16_SIGNATURE))
|
|
|| (Type != FAST486_TASK_RETURN)))
|
|
{
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Read the new TSS */
|
|
if (!Fast486ReadLinearMemory(State,
|
|
NewTssAddress,
|
|
&NewTss,
|
|
(NewTssDescriptor.Signature == FAST486_TSS_SIGNATURE)
|
|
|| (NewTssDescriptor.Signature == FAST486_BUSY_TSS_SIGNATURE)
|
|
? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
if (Type != FAST486_TASK_CALL)
|
|
{
|
|
/* Clear the busy bit of the outgoing task */
|
|
FAST486_SYSTEM_DESCRIPTOR OldTssDescriptor;
|
|
|
|
if (!Fast486ReadLinearMemory(State,
|
|
State->Gdtr.Address
|
|
+ GET_SEGMENT_INDEX(State->TaskReg.Selector),
|
|
&OldTssDescriptor,
|
|
sizeof(OldTssDescriptor),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
OldTssDescriptor.Signature = FAST486_TSS_SIGNATURE;
|
|
|
|
if (!Fast486WriteLinearMemory(State,
|
|
State->Gdtr.Address
|
|
+ GET_SEGMENT_INDEX(State->TaskReg.Selector),
|
|
&OldTssDescriptor,
|
|
sizeof(OldTssDescriptor),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* Store the link */
|
|
if ((NewTssDescriptor.Signature == FAST486_TSS_SIGNATURE)
|
|
|| (NewTssDescriptor.Signature == FAST486_BUSY_TSS_SIGNATURE))
|
|
{
|
|
NewTss.Link = State->TaskReg.Selector;
|
|
|
|
/* Write back the new TSS link */
|
|
if (!Fast486WriteLinearMemory(State,
|
|
NewTssAddress,
|
|
&NewTss.Link,
|
|
sizeof(NewTss.Link),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NewLegacyTss->Link = State->TaskReg.Selector;
|
|
|
|
/* Write back the new legacy TSS link */
|
|
if (!Fast486WriteLinearMemory(State,
|
|
NewTssAddress,
|
|
&NewLegacyTss->Link,
|
|
sizeof(NewLegacyTss->Link),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Save the current task into the TSS */
|
|
if (State->TaskReg.Modern)
|
|
{
|
|
OldTss.Cr3 = State->ControlRegisters[FAST486_REG_CR3];
|
|
OldTss.Eip = State->InstPtr.Long;
|
|
OldTss.Eflags = State->Flags.Long;
|
|
OldTss.Eax = State->GeneralRegs[FAST486_REG_EAX].Long;
|
|
OldTss.Ecx = State->GeneralRegs[FAST486_REG_ECX].Long;
|
|
OldTss.Edx = State->GeneralRegs[FAST486_REG_EDX].Long;
|
|
OldTss.Ebx = State->GeneralRegs[FAST486_REG_EBX].Long;
|
|
OldTss.Esp = State->GeneralRegs[FAST486_REG_ESP].Long;
|
|
OldTss.Ebp = State->GeneralRegs[FAST486_REG_EBP].Long;
|
|
OldTss.Esi = State->GeneralRegs[FAST486_REG_ESI].Long;
|
|
OldTss.Edi = State->GeneralRegs[FAST486_REG_EDI].Long;
|
|
OldTss.Es = State->SegmentRegs[FAST486_REG_ES].Selector;
|
|
OldTss.Cs = State->SegmentRegs[FAST486_REG_CS].Selector;
|
|
OldTss.Ss = State->SegmentRegs[FAST486_REG_SS].Selector;
|
|
OldTss.Ds = State->SegmentRegs[FAST486_REG_DS].Selector;
|
|
OldTss.Fs = State->SegmentRegs[FAST486_REG_FS].Selector;
|
|
OldTss.Gs = State->SegmentRegs[FAST486_REG_GS].Selector;
|
|
OldTss.Ldtr = State->Ldtr.Selector;
|
|
}
|
|
else
|
|
{
|
|
OldLegacyTss->Ip = State->InstPtr.LowWord;
|
|
OldLegacyTss->Flags = State->Flags.LowWord;
|
|
OldLegacyTss->Ax = State->GeneralRegs[FAST486_REG_EAX].LowWord;
|
|
OldLegacyTss->Cx = State->GeneralRegs[FAST486_REG_ECX].LowWord;
|
|
OldLegacyTss->Dx = State->GeneralRegs[FAST486_REG_EDX].LowWord;
|
|
OldLegacyTss->Bx = State->GeneralRegs[FAST486_REG_EBX].LowWord;
|
|
OldLegacyTss->Sp = State->GeneralRegs[FAST486_REG_ESP].LowWord;
|
|
OldLegacyTss->Bp = State->GeneralRegs[FAST486_REG_EBP].LowWord;
|
|
OldLegacyTss->Si = State->GeneralRegs[FAST486_REG_ESI].LowWord;
|
|
OldLegacyTss->Di = State->GeneralRegs[FAST486_REG_EDI].LowWord;
|
|
OldLegacyTss->Es = State->SegmentRegs[FAST486_REG_ES].Selector;
|
|
OldLegacyTss->Cs = State->SegmentRegs[FAST486_REG_CS].Selector;
|
|
OldLegacyTss->Ss = State->SegmentRegs[FAST486_REG_SS].Selector;
|
|
OldLegacyTss->Ds = State->SegmentRegs[FAST486_REG_DS].Selector;
|
|
OldLegacyTss->Ldtr = State->Ldtr.Selector;
|
|
}
|
|
|
|
/* Write back the old TSS */
|
|
if (!Fast486WriteLinearMemory(State,
|
|
State->TaskReg.Base,
|
|
&OldTss,
|
|
State->TaskReg.Modern
|
|
? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Mark the new task as busy */
|
|
if (NewTssDescriptor.Signature == FAST486_TSS_SIGNATURE
|
|
|| NewTssDescriptor.Signature == FAST486_BUSY_TSS_SIGNATURE)
|
|
{
|
|
/* 32-bit TSS */
|
|
NewTssDescriptor.Signature = FAST486_BUSY_TSS_SIGNATURE;
|
|
}
|
|
else
|
|
{
|
|
/* 16-bit TSS */
|
|
NewTssDescriptor.Signature = FAST486_BUSY_TSS_16_SIGNATURE;
|
|
}
|
|
|
|
/* Write back the new TSS descriptor */
|
|
if (!Fast486WriteLinearMemory(State,
|
|
State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
|
|
&NewTssDescriptor,
|
|
sizeof(NewTssDescriptor),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set the task switch bit */
|
|
State->ControlRegisters[FAST486_REG_CR0] |= FAST486_CR0_TS;
|
|
|
|
/* Load the task register with the new values */
|
|
State->TaskReg.Selector = Selector;
|
|
State->TaskReg.Base = NewTssAddress;
|
|
State->TaskReg.Limit = NewTssLimit;
|
|
State->TaskReg.Modern = (NewTssDescriptor.Signature == FAST486_BUSY_TSS_SIGNATURE);
|
|
|
|
if (NewTssDescriptor.Signature == FAST486_BUSY_TSS_SIGNATURE)
|
|
{
|
|
/* Change the page directory */
|
|
State->ControlRegisters[FAST486_REG_CR3] = NewTss.Cr3;
|
|
}
|
|
|
|
/* Flush the TLB */
|
|
Fast486FlushTlb(State);
|
|
|
|
/* Update the CPL */
|
|
if (NewTssDescriptor.Signature == FAST486_BUSY_TSS_SIGNATURE)
|
|
{
|
|
State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
|
|
}
|
|
else
|
|
{
|
|
State->Cpl = GET_SEGMENT_RPL(NewLegacyTss->Cs);
|
|
}
|
|
|
|
#ifndef FAST486_NO_PREFETCH
|
|
/* Context switching invalidates the prefetch */
|
|
State->PrefetchValid = FALSE;
|
|
#endif
|
|
|
|
/* Load the registers */
|
|
if (NewTssDescriptor.Signature == FAST486_BUSY_TSS_SIGNATURE)
|
|
{
|
|
State->InstPtr.Long = State->SavedInstPtr.Long = NewTss.Eip;
|
|
State->Flags.Long = NewTss.Eflags;
|
|
State->GeneralRegs[FAST486_REG_EAX].Long = NewTss.Eax;
|
|
State->GeneralRegs[FAST486_REG_ECX].Long = NewTss.Ecx;
|
|
State->GeneralRegs[FAST486_REG_EDX].Long = NewTss.Edx;
|
|
State->GeneralRegs[FAST486_REG_EBX].Long = NewTss.Ebx;
|
|
State->GeneralRegs[FAST486_REG_EBP].Long = NewTss.Ebp;
|
|
State->GeneralRegs[FAST486_REG_ESI].Long = NewTss.Esi;
|
|
State->GeneralRegs[FAST486_REG_EDI].Long = NewTss.Edi;
|
|
NewEs = NewTss.Es;
|
|
NewCs = NewTss.Cs;
|
|
NewDs = NewTss.Ds;
|
|
NewLdtr = NewTss.Ldtr;
|
|
|
|
if (Type == FAST486_TASK_CALL && State->Cpl < 3)
|
|
{
|
|
switch (State->Cpl)
|
|
{
|
|
case 0:
|
|
{
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewTss.Esp0;
|
|
NewSs = NewTss.Ss0;
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewTss.Esp1;
|
|
NewSs = NewTss.Ss1;
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewTss.Esp2;
|
|
NewSs = NewTss.Ss2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewTss.Esp;
|
|
NewSs = NewTss.Ss;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
State->InstPtr.LowWord = State->SavedInstPtr.LowWord = NewLegacyTss->Ip;
|
|
State->Flags.LowWord = NewLegacyTss->Flags;
|
|
State->GeneralRegs[FAST486_REG_EAX].LowWord = NewLegacyTss->Ax;
|
|
State->GeneralRegs[FAST486_REG_ECX].LowWord = NewLegacyTss->Cx;
|
|
State->GeneralRegs[FAST486_REG_EDX].LowWord = NewLegacyTss->Dx;
|
|
State->GeneralRegs[FAST486_REG_EBX].LowWord = NewLegacyTss->Bx;
|
|
State->GeneralRegs[FAST486_REG_EBP].LowWord = NewLegacyTss->Bp;
|
|
State->GeneralRegs[FAST486_REG_ESI].LowWord = NewLegacyTss->Si;
|
|
State->GeneralRegs[FAST486_REG_EDI].LowWord = NewLegacyTss->Di;
|
|
NewEs = NewLegacyTss->Es;
|
|
NewCs = NewLegacyTss->Cs;
|
|
NewDs = NewLegacyTss->Ds;
|
|
NewLdtr = NewLegacyTss->Ldtr;
|
|
|
|
if (Type == FAST486_TASK_CALL && State->Cpl < 3)
|
|
{
|
|
switch (State->Cpl)
|
|
{
|
|
case 0:
|
|
{
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewLegacyTss->Sp0;
|
|
NewSs = NewLegacyTss->Ss0;
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewLegacyTss->Sp1;
|
|
NewSs = NewLegacyTss->Ss1;
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewLegacyTss->Sp2;
|
|
NewSs = NewLegacyTss->Ss2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewLegacyTss->Sp;
|
|
NewSs = NewLegacyTss->Ss;
|
|
}
|
|
}
|
|
|
|
/* Set the NT flag if nesting */
|
|
if (Type == FAST486_TASK_CALL) State->Flags.Nt = TRUE;
|
|
|
|
if (GET_SEGMENT_INDEX(NewLdtr) != 0)
|
|
{
|
|
BOOLEAN Valid;
|
|
FAST486_SYSTEM_DESCRIPTOR GdtEntry;
|
|
|
|
if (NewLdtr & SEGMENT_TABLE_INDICATOR)
|
|
{
|
|
/* This selector doesn't point to the GDT */
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!Fast486ReadDescriptorEntry(State, NewLdtr, &Valid, (PFAST486_GDT_ENTRY)&GdtEntry))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
if (!Valid)
|
|
{
|
|
/* Invalid selector */
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
|
|
return FALSE;
|
|
}
|
|
|
|
if (GdtEntry.Signature != FAST486_LDT_SIGNATURE)
|
|
{
|
|
/* This is not an LDT descriptor */
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!GdtEntry.Present)
|
|
{
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Update the LDTR */
|
|
State->Ldtr.Selector = NewLdtr;
|
|
State->Ldtr.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
|
|
State->Ldtr.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
|
|
|
|
if (GdtEntry.Granularity)
|
|
{
|
|
State->Ldtr.Limit <<= 12;
|
|
State->Ldtr.Limit |= 0x00000FFF;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/* The LDT of this task is empty */
|
|
RtlZeroMemory(&State->Ldtr, sizeof(State->Ldtr));
|
|
}
|
|
|
|
/* Load the new segments */
|
|
if (!Fast486LoadSegmentInternal(State, FAST486_REG_CS, NewCs, FAST486_EXCEPTION_TS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!Fast486LoadSegmentInternal(State, FAST486_REG_SS, NewSs, FAST486_EXCEPTION_TS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!Fast486LoadSegmentInternal(State, FAST486_REG_ES, NewEs, FAST486_EXCEPTION_TS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!Fast486LoadSegmentInternal(State, FAST486_REG_DS, NewDs, FAST486_EXCEPTION_TS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (NewTssDescriptor.Signature == FAST486_BUSY_TSS_SIGNATURE)
|
|
{
|
|
if (!Fast486LoadSegmentInternal(State,
|
|
FAST486_REG_FS,
|
|
NewTss.Fs,
|
|
FAST486_EXCEPTION_TS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!Fast486LoadSegmentInternal(State,
|
|
FAST486_REG_GS,
|
|
NewTss.Gs,
|
|
FAST486_EXCEPTION_TS))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOLEAN
|
|
FASTCALL
|
|
Fast486CallGate(PFAST486_STATE State,
|
|
PFAST486_CALL_GATE Gate,
|
|
BOOLEAN Call)
|
|
{
|
|
BOOLEAN Valid;
|
|
FAST486_GDT_ENTRY NewCodeSegment;
|
|
BOOLEAN GateSize = (Gate->Type == FAST486_CALL_GATE_SIGNATURE);
|
|
FAST486_TSS Tss;
|
|
PFAST486_LEGACY_TSS LegacyTss = (PFAST486_LEGACY_TSS)&Tss;
|
|
USHORT OldCs = State->SegmentRegs[FAST486_REG_CS].Selector;
|
|
ULONG OldEip = State->InstPtr.Long;
|
|
USHORT OldCpl = State->Cpl;
|
|
USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
|
|
ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
|
|
ULONG ParamBuffer[32]; /* Maximum possible size - 32 DWORDs */
|
|
PULONG LongParams = (PULONG)ParamBuffer;
|
|
PUSHORT ShortParams = (PUSHORT)ParamBuffer;
|
|
|
|
if (!Gate->Selector)
|
|
{
|
|
/* The code segment is NULL */
|
|
Fast486Exception(State, FAST486_EXCEPTION_GP);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!Fast486ReadDescriptorEntry(State, Gate->Selector, &Valid, &NewCodeSegment))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
if (!Valid || (NewCodeSegment.Dpl > Fast486GetCurrentPrivLevel(State)))
|
|
{
|
|
/* Code segment invalid */
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Gate->Selector);
|
|
return FALSE;
|
|
}
|
|
|
|
if (Call && Gate->ParamCount)
|
|
{
|
|
/* Read the parameters */
|
|
if (!Fast486ReadMemory(State,
|
|
FAST486_REG_SS,
|
|
OldEsp,
|
|
FALSE,
|
|
ParamBuffer,
|
|
Gate->ParamCount * (GateSize ? sizeof(ULONG) : sizeof(USHORT))))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Check if the new code segment is more privileged */
|
|
if (NewCodeSegment.Dpl < OldCpl)
|
|
{
|
|
if (Call)
|
|
{
|
|
USHORT NewSs;
|
|
ULONG NewEsp;
|
|
|
|
/* Read the TSS */
|
|
if (!Fast486ReadLinearMemory(State,
|
|
State->TaskReg.Base,
|
|
&Tss,
|
|
State->TaskReg.Modern
|
|
? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
|
|
FALSE))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Switch to the new privilege level */
|
|
State->Cpl = NewCodeSegment.Dpl;
|
|
|
|
/* Check the new (higher) privilege level */
|
|
switch (State->Cpl)
|
|
{
|
|
case 0:
|
|
{
|
|
if (State->TaskReg.Modern)
|
|
{
|
|
NewSs = Tss.Ss0;
|
|
NewEsp = Tss.Esp0;
|
|
}
|
|
else
|
|
{
|
|
NewSs = LegacyTss->Ss0;
|
|
NewEsp = LegacyTss->Sp0;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 1:
|
|
{
|
|
if (State->TaskReg.Modern)
|
|
{
|
|
NewSs = Tss.Ss1;
|
|
NewEsp = Tss.Esp1;
|
|
}
|
|
else
|
|
{
|
|
NewSs = LegacyTss->Ss1;
|
|
NewEsp = LegacyTss->Sp1;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case 2:
|
|
{
|
|
if (State->TaskReg.Modern)
|
|
{
|
|
NewSs = Tss.Ss2;
|
|
NewEsp = Tss.Esp2;
|
|
}
|
|
else
|
|
{
|
|
NewSs = LegacyTss->Ss2;
|
|
NewEsp = LegacyTss->Sp2;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
/* Should never reach here! */
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
if (!Fast486LoadSegment(State, FAST486_REG_SS, NewSs))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
|
|
State->GeneralRegs[FAST486_REG_ESP].Long = NewEsp;
|
|
}
|
|
else if (!NewCodeSegment.DirConf)
|
|
{
|
|
/* This is not allowed for jumps */
|
|
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Gate->Selector);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Load new CS */
|
|
if (!Fast486LoadSegment(State, FAST486_REG_CS, Gate->Selector))
|
|
{
|
|
/* An exception occurred during the jump */
|
|
return FALSE;
|
|
}
|
|
|
|
/* Set the instruction pointer */
|
|
if (GateSize) State->InstPtr.Long = MAKELONG(Gate->Offset, Gate->OffsetHigh);
|
|
else State->InstPtr.Long = Gate->Offset;
|
|
|
|
if (Call)
|
|
{
|
|
INT i;
|
|
|
|
/* Check if the new code segment is more privileged (again) */
|
|
if (NewCodeSegment.Dpl < OldCpl)
|
|
{
|
|
/* Push SS selector */
|
|
if (!Fast486StackPushInternal(State, GateSize, OldSs)) return FALSE;
|
|
|
|
/* Push stack pointer */
|
|
if (!Fast486StackPushInternal(State, GateSize, OldEsp)) return FALSE;
|
|
}
|
|
|
|
/* Push the parameters in reverse order */
|
|
for (i = Gate->ParamCount - 1; i >= 0; i--)
|
|
{
|
|
if (!Fast486StackPushInternal(State,
|
|
GateSize,
|
|
GateSize ? LongParams[i] : ShortParams[i]))
|
|
{
|
|
/* Exception occurred */
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Push CS selector */
|
|
if (!Fast486StackPushInternal(State, GateSize, OldCs)) return FALSE;
|
|
|
|
/* Push the instruction pointer */
|
|
if (!Fast486StackPushInternal(State, GateSize, OldEip)) return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* EOF */
|