* Fast486 386/486 CPU Emulation Library
* common.c
*
- * Copyright (C) 2014 Aleksandar Andrejevic <theflash AT sdf DOT lonestar DOT org>
+ * 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
/* PUBLIC FUNCTIONS ***********************************************************/
BOOLEAN
+FASTCALL
Fast486ReadMemory(PFAST486_STATE State,
FAST486_SEG_REGS SegmentReg,
ULONG Offset,
/* Get the cached descriptor */
CachedDescriptor = &State->SegmentRegs[SegmentReg];
- if ((Offset + Size - 1) > CachedDescriptor->Limit)
+ if (InstFetch || CachedDescriptor->Executable || !CachedDescriptor->DirConf)
{
- /* Read beyond limit */
- Fast486Exception(State, FAST486_EXCEPTION_GP);
- return FALSE;
+ if ((Offset + Size - 1) > CachedDescriptor->Limit)
+ {
+ /* Read beyond limit */
+ Fast486Exception(State, FAST486_EXCEPTION_GP);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (Offset < CachedDescriptor->Limit)
+ {
+ /* Read beyond limit */
+ Fast486Exception(State, FAST486_EXCEPTION_GP);
+ return FALSE;
+ }
}
/* Check for protected mode */
- if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
+ if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
{
/* Privilege checks */
{
/* We can't prefetch without possibly violating page permissions */
State->PrefetchValid = FALSE;
- return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
+ return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size, TRUE);
}
}
if (Fast486ReadLinearMemory(State,
State->PrefetchAddress,
State->PrefetchCache,
- FAST486_CACHE_SIZE))
+ FAST486_CACHE_SIZE,
+ TRUE))
{
State->PrefetchValid = TRUE;
#endif
{
/* Read from the linear address */
- return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size);
+ return Fast486ReadLinearMemory(State, LinearAddress, Buffer, Size, TRUE);
}
}
BOOLEAN
+FASTCALL
Fast486WriteMemory(PFAST486_STATE State,
FAST486_SEG_REGS SegmentReg,
ULONG Offset,
/* Get the cached descriptor */
CachedDescriptor = &State->SegmentRegs[SegmentReg];
- if ((Offset + Size - 1) > CachedDescriptor->Limit)
+ if (CachedDescriptor->Executable || !CachedDescriptor->DirConf)
{
- /* Write beyond limit */
- Fast486Exception(State, FAST486_EXCEPTION_GP);
- return FALSE;
+ if ((Offset + Size - 1) > CachedDescriptor->Limit)
+ {
+ /* Write beyond limit */
+ Fast486Exception(State, FAST486_EXCEPTION_GP);
+ return FALSE;
+ }
+ }
+ else
+ {
+ if (Offset < CachedDescriptor->Limit)
+ {
+ /* Read beyond limit */
+ Fast486Exception(State, FAST486_EXCEPTION_GP);
+ return FALSE;
+ }
}
/* Check for protected mode */
- if (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE)
+ if ((State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE) && !State->Flags.Vm)
{
/* Privilege checks */
#endif
/* Write to the linear address */
- return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size);
+ return Fast486WriteLinearMemory(State, LinearAddress, Buffer, Size, TRUE);
}
static inline BOOLEAN
State->Idtr.Address
+ Number * sizeof(*IdtEntry),
IdtEntry,
- sizeof(*IdtEntry)))
+ sizeof(*IdtEntry),
+ FALSE))
{
/* Exception occurred */
return FALSE;
static inline BOOLEAN
FASTCALL
Fast486InterruptInternal(PFAST486_STATE State,
- PFAST486_IDT_ENTRY IdtEntry)
+ PFAST486_IDT_ENTRY IdtEntry,
+ BOOLEAN PushErrorCode,
+ ULONG ErrorCode)
{
- USHORT SegmentSelector = IdtEntry->Selector;
- ULONG Offset = MAKELONG(IdtEntry->Offset, IdtEntry->OffsetHigh);
- ULONG GateType = IdtEntry->Type;
- BOOLEAN GateSize = (GateType == FAST486_IDT_INT_GATE_32) ||
- (GateType == FAST486_IDT_TRAP_GATE_32);
-
- BOOLEAN Success = FALSE;
- ULONG OldPrefixFlags = State->PrefixFlags;
+ 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)
{
- FAST486_TSS Tss;
USHORT OldSs = State->SegmentRegs[FAST486_REG_SS].Selector;
ULONG OldEsp = State->GeneralRegs[FAST486_REG_ESP].Long;
+ BOOLEAN OldVm = State->Flags.Vm;
- if (GateType == FAST486_TASK_GATE_SIGNATURE)
+ if (IdtEntry->Type == FAST486_TASK_GATE_SIGNATURE)
{
/* Task call */
return Fast486TaskSwitch(State, FAST486_TASK_CALL, IdtEntry->Selector);
}
-
- if (GateSize != (State->SegmentRegs[FAST486_REG_CS].Size))
- {
- /* The gate size doesn't match the current operand size, so set the OPSIZE flag. */
- State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
- }
/* Check if the interrupt handler is more privileged or if we're in V86 mode */
- if ((Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
- || State->Flags.Vm)
+ 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,
- sizeof(Tss)))
+ State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1)
+ ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
+ FALSE))
{
/* Exception occurred */
- goto Cleanup;
+ return FALSE;
}
/* Switch to the new privilege level */
- State->Cpl = GET_SEGMENT_RPL(SegmentSelector);
-
- if (State->Flags.Vm)
- {
- /* Clear the VM flag */
- State->Flags.Vm = FALSE;
-
- /* Push GS, FS, DS and ES */
- if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_GS].Selector)) goto Cleanup;
- if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_FS].Selector)) goto Cleanup;
- if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_DS].Selector)) goto Cleanup;
- if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_ES].Selector)) goto Cleanup;
-
- /* Now load them with NULL selectors, since they are useless in protected mode */
- if (!Fast486LoadSegment(State, FAST486_REG_GS, 0)) goto Cleanup;
- if (!Fast486LoadSegment(State, FAST486_REG_FS, 0)) goto Cleanup;
- if (!Fast486LoadSegment(State, FAST486_REG_DS, 0)) goto Cleanup;
- if (!Fast486LoadSegment(State, FAST486_REG_ES, 0)) goto Cleanup;
- }
+ State->Cpl = GET_SEGMENT_RPL(IdtEntry->Selector);
/* Check the new (higher) privilege level */
switch (State->Cpl)
{
case 0:
{
- if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss0))
+ if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
{
- /* Exception occurred */
- goto Cleanup;
+ NewSs = Tss.Ss0;
+ NewEsp = Tss.Esp0;
+ }
+ else
+ {
+ NewSs = LegacyTss->Ss0;
+ NewEsp = LegacyTss->Sp0;
}
- State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp0;
break;
}
case 1:
{
- if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss1))
+ if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
+ {
+ NewSs = Tss.Ss1;
+ NewEsp = Tss.Esp1;
+ }
+ else
{
- /* Exception occurred */
- goto Cleanup;
+ NewSs = LegacyTss->Ss1;
+ NewEsp = LegacyTss->Sp1;
}
- State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp1;
break;
}
case 2:
{
- if (!Fast486LoadSegment(State, FAST486_REG_SS, Tss.Ss2))
+ if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
{
- /* Exception occurred */
- goto Cleanup;
+ NewSs = Tss.Ss2;
+ NewEsp = Tss.Esp2;
+ }
+ else
+ {
+ NewSs = LegacyTss->Ss2;
+ NewEsp = LegacyTss->Sp2;
}
- State->GeneralRegs[FAST486_REG_ESP].Long = Tss.Esp2;
break;
}
}
}
+ if (!Fast486LoadSegment(State, FAST486_REG_SS, NewSs))
+ {
+ /* Exception occurred */
+ return FALSE;
+ }
+
+ State->GeneralRegs[FAST486_REG_ESP].Long = NewEsp;
+
+ if (State->Flags.Vm)
+ {
+ /* Clear the VM flag */
+ State->Flags.Vm = FALSE;
+ }
+ }
+
+ /* 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 (!Fast486StackPush(State, OldSs)) goto Cleanup;
+ if (!Fast486StackPushInternal(State, GateSize, OldSs)) return FALSE;
- /* Push stack pointer */
- if (!Fast486StackPush(State, OldEsp)) goto Cleanup;
+ /* Push the stack pointer */
+ if (!Fast486StackPushInternal(State, GateSize, OldEsp)) return FALSE;
}
}
else
{
- if (State->SegmentRegs[FAST486_REG_CS].Size)
+ /* Load new CS */
+ if (!Fast486LoadSegment(State, FAST486_REG_CS, IdtEntry->Selector))
{
- /* Set OPSIZE, because INT always pushes 16-bit values in real mode */
- State->PrefixFlags |= FAST486_PREFIX_OPSIZE;
+ /* An exception occurred during the jump */
+ return FALSE;
}
+
+ /* Set the new IP */
+ State->InstPtr.LowWord = IdtEntry->Offset;
}
/* Push EFLAGS */
- if (!Fast486StackPush(State, State->Flags.Long)) goto Cleanup;
+ if (!Fast486StackPushInternal(State, GateSize, OldFlags)) return FALSE;
/* Push CS selector */
- if (!Fast486StackPush(State, State->SegmentRegs[FAST486_REG_CS].Selector)) goto Cleanup;
+ if (!Fast486StackPushInternal(State, GateSize, OldCs)) return FALSE;
/* Push the instruction pointer */
- if (!Fast486StackPush(State, State->InstPtr.Long)) goto Cleanup;
-
- if ((GateType == FAST486_IDT_INT_GATE) || (GateType == FAST486_IDT_INT_GATE_32))
- {
- /* Disable interrupts after a jump to an interrupt gate handler */
- State->Flags.If = FALSE;
- }
+ if (!Fast486StackPushInternal(State, GateSize, OldEip)) return FALSE;
- /* Load new CS */
- if (!Fast486LoadSegment(State, FAST486_REG_CS, SegmentSelector))
+ if (PushErrorCode)
{
- /* An exception occurred during the jump */
- goto Cleanup;
+ /* Push the error code */
+ if (!Fast486StackPushInternal(State, GateSize, ErrorCode)) return FALSE;
}
- if (GateSize)
- {
- /* 32-bit code segment, use EIP */
- State->InstPtr.Long = Offset;
- }
- else
+ if ((IdtEntry->Type == FAST486_IDT_INT_GATE)
+ || (IdtEntry->Type == FAST486_IDT_INT_GATE_32))
{
- /* 16-bit code segment, use IP */
- State->InstPtr.LowWord = LOWORD(Offset);
+ /* Disable interrupts after a jump to an interrupt gate handler */
+ State->Flags.If = FALSE;
}
- Success = TRUE;
+ /* Clear TF */
+ State->Flags.Tf = FALSE;
-Cleanup:
- /* Restore the prefix flags */
- State->PrefixFlags = OldPrefixFlags;
-
- return Success;
+ return TRUE;
}
BOOLEAN
}
/* Perform the interrupt */
- if (!Fast486InterruptInternal(State, &IdtEntry))
+ if (!Fast486InterruptInternal(State, &IdtEntry, FALSE, 0))
{
/* Exception occurred */
return FALSE;
FAST486_EXCEPTIONS ExceptionCode,
ULONG ErrorCode)
{
+ FAST486_IDT_ENTRY IdtEntry;
+
/* Increment the exception count */
State->ExceptionCount++;
/* Restore the IP to the saved IP */
State->InstPtr = State->SavedInstPtr;
- /* Perform the interrupt */
- if (!Fast486PerformInterrupt(State, ExceptionCode))
+ /* 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
return;
}
- if (EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
- && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE))
+ /* Perform the interrupt */
+ if (!Fast486InterruptInternal(State,
+ &IdtEntry,
+ EXCEPTION_HAS_ERROR_CODE(ExceptionCode)
+ && (State->ControlRegisters[FAST486_REG_CR0] & FAST486_CR0_PE),
+ ErrorCode))
{
- /* Push the error code */
- if (!Fast486StackPush(State, ErrorCode))
- {
- /*
- * If this function failed, that means Fast486Exception
- * was called again, so just return in this case.
- */
- return;
- }
+ /*
+ * If this function failed, that means Fast486Exception
+ * was called again, so just return in this case.
+ */
+ return;
}
/* Reset the exception count */
{
ULONG NewTssAddress;
ULONG NewTssLimit;
+ FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor;
FAST486_TSS OldTss;
+ PFAST486_LEGACY_TSS OldLegacyTss = (PFAST486_LEGACY_TSS)&OldTss;
FAST486_TSS NewTss;
- FAST486_SYSTEM_DESCRIPTOR NewTssDescriptor;
+ PFAST486_LEGACY_TSS NewLegacyTss = (PFAST486_LEGACY_TSS)&NewTss;
+ USHORT NewLdtr, NewEs, NewCs, NewSs, NewDs;
+
+ if (State->TaskReg.Limit < (sizeof(FAST486_TSS) - 1)
+ && 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,
- sizeof(OldTss)))
+ State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1)
+ ? 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) Selector = LOWORD(OldTss.Link);
+ if (Type == FAST486_TASK_RETURN)
+ {
+ if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1)) 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 + 1))
+ || GET_SEGMENT_INDEX(Selector) >= (State->Gdtr.Size + 1u))
{
Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
return FALSE;
if (!Fast486ReadLinearMemory(State,
State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
&NewTssDescriptor,
- sizeof(NewTssDescriptor)))
+ sizeof(NewTssDescriptor),
+ FALSE))
{
/* Exception occurred */
return FALSE;
NewTssLimit |= 0x00000FFF;
}
- if (NewTssLimit < sizeof(FAST486_TSS))
+ if (NewTssLimit < (sizeof(FAST486_TSS) - 1)
+ && NewTssLimit != (sizeof(FAST486_LEGACY_TSS) - 1))
{
- /* TSS limit too small */
+ /* 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)
+ 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_SIGNATURE)
+ && (NewTssDescriptor.Signature != FAST486_BUSY_TSS_16_SIGNATURE))
|| (Type != FAST486_TASK_RETURN)))
{
- Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
+ Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
return FALSE;
}
if (!Fast486ReadLinearMemory(State,
NewTssAddress,
&NewTss,
- sizeof(NewTss)))
+ NewTssLimit >= (sizeof(FAST486_TSS) - 1)
+ ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
+ FALSE))
{
/* Exception occurred */
return FALSE;
State->Gdtr.Address
+ GET_SEGMENT_INDEX(State->TaskReg.Selector),
&OldTssDescriptor,
- sizeof(OldTssDescriptor)))
+ sizeof(OldTssDescriptor),
+ FALSE))
{
/* Exception occurred */
return FALSE;
State->Gdtr.Address
+ GET_SEGMENT_INDEX(State->TaskReg.Selector),
&OldTssDescriptor,
- sizeof(OldTssDescriptor)))
+ sizeof(OldTssDescriptor),
+ FALSE))
{
/* Exception occurred */
return FALSE;
else
{
/* Store the link */
- NewTss.Link = State->TaskReg.Selector;
+ if (NewTssLimit >= (sizeof(FAST486_TSS) - 1)) NewTss.Link = State->TaskReg.Selector;
+ else NewLegacyTss->Link = State->TaskReg.Selector;
}
/* Save the current task into the TSS */
- 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;
+ if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
+ {
+ 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,
- sizeof(OldTss)))
+ State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1)
+ ? sizeof(FAST486_TSS) : sizeof(FAST486_LEGACY_TSS),
+ FALSE))
{
/* Exception occurred */
return FALSE;
if (!Fast486WriteLinearMemory(State,
State->Gdtr.Address + GET_SEGMENT_INDEX(Selector),
&NewTssDescriptor,
- sizeof(NewTssDescriptor)))
+ sizeof(NewTssDescriptor),
+ FALSE))
{
/* Exception occurred */
return FALSE;
State->TaskReg.Base = NewTssAddress;
State->TaskReg.Limit = NewTssLimit;
- /* Change the page directory */
- State->ControlRegisters[FAST486_REG_CR3] = NewTss.Cr3;
+ if (NewTssLimit >= (sizeof(FAST486_TSS) - 1))
+ {
+ /* Change the page directory */
+ State->ControlRegisters[FAST486_REG_CR3] = NewTss.Cr3;
+ }
/* Flush the TLB */
- if (State->Tlb) RtlZeroMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG));
+ Fast486FlushTlb(State);
+
+ /* Update the CPL */
+ if (NewTssLimit >= (sizeof(FAST486_TSS) - 1)) State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
+ else State->Cpl = GET_SEGMENT_RPL(NewLegacyTss->Cs);
#ifndef FAST486_NO_PREFETCH
/* Context switching invalidates the prefetch */
#endif
/* Load the registers */
- 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_ESP].Long = NewTss.Esp;
- State->GeneralRegs[FAST486_REG_EBP].Long = NewTss.Ebp;
- State->GeneralRegs[FAST486_REG_ESI].Long = NewTss.Esi;
- State->GeneralRegs[FAST486_REG_EDI].Long = NewTss.Edi;
+ if (NewTssLimit >= (sizeof(FAST486_TSS) - 1))
+ {
+ 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_ESP].Long = NewTss.Esp;
+ 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;
+ NewSs = NewTss.Ss;
+ NewDs = NewTss.Ds;
+ NewLdtr = NewTss.Ldtr;
+ }
+ 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_ESP].LowWord = NewLegacyTss->Sp;
+ 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;
+ NewSs = NewLegacyTss->Ss;
+ NewDs = NewLegacyTss->Ds;
+ NewLdtr = NewLegacyTss->Ldtr;
+ }
/* Set the NT flag if nesting */
if (Type == FAST486_TASK_CALL) State->Flags.Nt = TRUE;
- if (GET_SEGMENT_INDEX(NewTss.Ldtr) != 0)
+ if (GET_SEGMENT_INDEX(NewLdtr) != 0)
{
BOOLEAN Valid;
FAST486_SYSTEM_DESCRIPTOR GdtEntry;
- if (NewTss.Ldtr & SEGMENT_TABLE_INDICATOR)
+ if (NewLdtr & SEGMENT_TABLE_INDICATOR)
{
/* This selector doesn't point to the GDT */
- Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+ Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
return FALSE;
}
- if (!Fast486ReadDescriptorEntry(State,
- NewTss.Ldtr,
- &Valid,
- (PFAST486_GDT_ENTRY)&GdtEntry))
+ if (!Fast486ReadDescriptorEntry(State, NewLdtr, &Valid, (PFAST486_GDT_ENTRY)&GdtEntry))
{
/* Exception occurred */
return FALSE;
if (!Valid)
{
/* Invalid selector */
- Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+ 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, NewTss.Ldtr);
+ Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
return FALSE;
}
if (!GdtEntry.Present)
{
- Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewTss.Ldtr);
+ Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, NewLdtr);
return FALSE;
}
/* Update the LDTR */
- State->Ldtr.Selector = NewTss.Ldtr;
+ State->Ldtr.Selector = NewLdtr;
State->Ldtr.Base = GdtEntry.Base | (GdtEntry.BaseMid << 16) | (GdtEntry.BaseHigh << 24);
State->Ldtr.Limit = GdtEntry.Limit | (GdtEntry.LimitHigh << 16);
}
/* Load the new segments */
- if (!Fast486LoadSegmentInternal(State,
- FAST486_REG_CS,
- NewTss.Cs,
- FAST486_EXCEPTION_TS))
+ 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_SS,
- NewTss.Ss,
- FAST486_EXCEPTION_TS))
+ if (!Fast486LoadSegmentInternal(State, FAST486_REG_ES, NewEs, FAST486_EXCEPTION_TS))
{
return FALSE;
}
- if (!Fast486LoadSegmentInternal(State,
- FAST486_REG_ES,
- NewTss.Es,
- FAST486_EXCEPTION_TS))
+ if (!Fast486LoadSegmentInternal(State, FAST486_REG_DS, NewDs, FAST486_EXCEPTION_TS))
{
return FALSE;
}
- if (!Fast486LoadSegmentInternal(State,
- FAST486_REG_DS,
- NewTss.Ds,
- FAST486_EXCEPTION_TS))
+ if (NewTssLimit >= (sizeof(FAST486_TSS) - 1))
{
+ 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 (!Fast486LoadSegmentInternal(State,
- FAST486_REG_FS,
- NewTss.Fs,
- FAST486_EXCEPTION_TS))
+ if (!Fast486ReadDescriptorEntry(State, Gate->Selector, &Valid, &NewCodeSegment))
{
+ /* Exception occurred */
return FALSE;
}
- if (!Fast486LoadSegmentInternal(State,
- FAST486_REG_GS,
- NewTss.Gs,
- FAST486_EXCEPTION_TS))
+ 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.Limit >= (sizeof(FAST486_TSS) - 1)
+ ? 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.Limit >= (sizeof(FAST486_TSS) - 1))
+ {
+ NewSs = Tss.Ss0;
+ NewEsp = Tss.Esp0;
+ }
+ else
+ {
+ NewSs = LegacyTss->Ss0;
+ NewEsp = LegacyTss->Sp0;
+ }
+
+ break;
+ }
+
+ case 1:
+ {
+ if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
+ {
+ NewSs = Tss.Ss1;
+ NewEsp = Tss.Esp1;
+ }
+ else
+ {
+ NewSs = LegacyTss->Ss1;
+ NewEsp = LegacyTss->Sp1;
+ }
+
+ break;
+ }
+
+ case 2:
+ {
+ if (State->TaskReg.Limit >= (sizeof(FAST486_TSS) - 1))
+ {
+ 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;
}