[FAST486]
[reactos.git] / reactos / lib / fast486 / common.c
index c776d2f..4f78e01 100644 (file)
@@ -2,7 +2,7 @@
  * 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
@@ -47,11 +47,23 @@ Fast486ReadMemory(PFAST486_STATE State,
     /* Get the cached descriptor */
     CachedDescriptor = &State->SegmentRegs[SegmentReg];
 
-    if ((Offset + Size - 1) > CachedDescriptor->Limit)
+    if (InstFetch || !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 */
@@ -106,6 +118,13 @@ Fast486ReadMemory(PFAST486_STATE State,
             /* 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);
+            }
         }
 
         /* Prefetch */
@@ -150,11 +169,23 @@ Fast486WriteMemory(PFAST486_STATE State,
     /* Get the cached descriptor */
     CachedDescriptor = &State->SegmentRegs[SegmentReg];
 
-    if ((Offset + Size - 1) > CachedDescriptor->Limit)
+    if (!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 */
@@ -257,7 +288,9 @@ Fast486GetIntVector(PFAST486_STATE State,
 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);
@@ -280,18 +313,16 @@ Fast486InterruptInternal(PFAST486_STATE State,
             /* 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 toggle
-             * the OPSIZE flag.
-             */
-            State->PrefixFlags ^= FAST486_PREFIX_OPSIZE;
+            /* 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 */
-        if (Fast486GetCurrentPrivLevel(State) > GET_SEGMENT_RPL(SegmentSelector))
+        /* 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)
         {
             /* Read the TSS */
             if (!Fast486ReadLinearMemory(State,
@@ -303,8 +334,29 @@ Fast486InterruptInternal(PFAST486_STATE State,
                 goto Cleanup;
             }
 
+            /* 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;
+            }
+
             /* Check the new (higher) privilege level */
-            switch (GET_SEGMENT_RPL(SegmentSelector))
+            switch (State->Cpl)
             {
                 case 0:
                 {
@@ -374,6 +426,16 @@ Fast486InterruptInternal(PFAST486_STATE State,
     /* Push the instruction pointer */
     if (!Fast486StackPush(State, State->InstPtr.Long)) goto Cleanup;
 
+    if (PushErrorCode)
+    {
+        /* Push the error code */
+        if (!Fast486StackPush(State, ErrorCode))
+        {
+            /* An exception occurred */
+            goto Cleanup;
+        }
+    }
+
     if ((GateType == FAST486_IDT_INT_GATE) || (GateType == FAST486_IDT_INT_GATE_32))
     {
         /* Disable interrupts after a jump to an interrupt gate handler */
@@ -422,7 +484,7 @@ Fast486PerformInterrupt(PFAST486_STATE State,
     }
 
     /* Perform the interrupt */
-    if (!Fast486InterruptInternal(State, &IdtEntry))
+    if (!Fast486InterruptInternal(State, &IdtEntry, FALSE, 0))
     {
         /* Exception occurred */
         return FALSE;
@@ -437,6 +499,8 @@ Fast486ExceptionWithErrorCode(PFAST486_STATE State,
                               FAST486_EXCEPTIONS ExceptionCode,
                               ULONG ErrorCode)
 {
+    FAST486_IDT_ENTRY IdtEntry;
+
     /* Increment the exception count */
     State->ExceptionCount++;
 
@@ -459,11 +523,14 @@ Fast486ExceptionWithErrorCode(PFAST486_STATE State,
         return;
     }
 
+    /* Clear the prefix flags */
+    State->PrefixFlags = 0;
+
     /* Restore the IP to the saved IP */
     State->InstPtr = State->SavedInstPtr;
 
-    /* Perform the interrupt */
-    if (!Fast486PerformInterrupt(State, ExceptionCode))
+    /* Get the interrupt vector */
+    if (!Fast486GetIntVector(State, ExceptionCode, &IdtEntry))
     {
         /*
          * If this function failed, that means Fast486Exception
@@ -472,18 +539,18 @@ Fast486ExceptionWithErrorCode(PFAST486_STATE State,
         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 */
@@ -516,7 +583,7 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
     /* 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;
@@ -546,12 +613,18 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
 
     /* Calculate the limit of the new TSS */
     NewTssLimit = NewTssDescriptor.Limit | (NewTssDescriptor.LimitHigh << 16);
-    if (NewTssDescriptor.Granularity) NewTssLimit <<= 12;
+
+    if (NewTssDescriptor.Granularity)
+    {
+        NewTssLimit <<= 12;
+        NewTssLimit |= 0x00000FFF;
+    }
 
     if (NewTssLimit < sizeof(FAST486_TSS))
     {
         /* TSS limit too small */
-        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector); 
+        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_TS, Selector);
+        return FALSE;
     }
 
     /*
@@ -563,7 +636,7 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
         && ((NewTssDescriptor.Signature != FAST486_BUSY_TSS_SIGNATURE)
         || (Type != FAST486_TASK_RETURN)))
     {
-        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector); 
+        Fast486ExceptionWithErrorCode(State, FAST486_EXCEPTION_GP, Selector);
         return FALSE;
     }
 
@@ -667,11 +740,17 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
     /* Flush the TLB */
     if (State->Tlb) RtlZeroMemory(State->Tlb, NUM_TLB_ENTRIES * sizeof(ULONG));
 
+    /* Update the CPL */
+    State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
+
 #ifndef FAST486_NO_PREFETCH
     /* Context switching invalidates the prefetch */
     State->PrefetchValid = FALSE;
 #endif
 
+    /* Update the CPL */
+    State->Cpl = GET_SEGMENT_RPL(NewTss.Cs);
+
     /* Load the registers */
     State->InstPtr.Long = State->SavedInstPtr.Long = NewTss.Eip;
     State->Flags.Long = NewTss.Eflags;
@@ -699,10 +778,10 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
             return FALSE;
         }
 
-        if (Fast486ReadDescriptorEntry(State,
-                                       NewTss.Ldtr,
-                                       &Valid,
-                                       (PFAST486_GDT_ENTRY)&GdtEntry))
+        if (!Fast486ReadDescriptorEntry(State,
+                                        NewTss.Ldtr,
+                                        &Valid,
+                                        (PFAST486_GDT_ENTRY)&GdtEntry))
         {
             /* Exception occurred */
             return FALSE;
@@ -732,7 +811,12 @@ Fast486TaskSwitch(PFAST486_STATE State, FAST486_TASK_SWITCH_TYPE Type, USHORT Se
         State->Ldtr.Selector = NewTss.Ldtr;
         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;
+
+        if (GdtEntry.Granularity)
+        {
+            State->Ldtr.Limit <<= 12;
+            State->Ldtr.Limit |= 0x00000FFF;
+        }
     }
     else
     {