Index: trunk/src/VBox/VMM/VMMAll/IEMAll.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/IEMAll.cpp	(revision 39931)
+++ trunk/src/VBox/VMM/VMMAll/IEMAll.cpp	(revision 39945)
@@ -550,4 +550,5 @@
 static VBOXSTRICTRC     iemRaiseSelectorNotPresentBySelector(PIEMCPU pIemCpu, uint16_t uSel);
 static VBOXSTRICTRC     iemRaiseSelectorNotPresentWithErr(PIEMCPU pIemCpu, uint16_t uErr);
+static VBOXSTRICTRC     iemRaiseStackSelectorNotPresentBySelector(PIEMCPU pIemCpu, uint16_t uSel);
 static VBOXSTRICTRC     iemRaiseGeneralProtectionFault(PIEMCPU pIemCpu, uint16_t uErr);
 static VBOXSTRICTRC     iemRaiseGeneralProtectionFault0(PIEMCPU pIemCpu);
@@ -561,4 +562,6 @@
 static VBOXSTRICTRC     iemMemFetchDataU32(PIEMCPU pIemCpu, uint32_t *pu32Dst, uint8_t iSegReg, RTGCPTR GCPtrMem);
 static VBOXSTRICTRC     iemMemFetchDataU64(PIEMCPU pIemCpu, uint64_t *pu64Dst, uint8_t iSegReg, RTGCPTR GCPtrMem);
+static VBOXSTRICTRC     iemMemFetchSysU32(PIEMCPU pIemCpu, uint32_t *pu32Dst, uint8_t iSegReg, RTGCPTR GCPtrMem);
+static VBOXSTRICTRC     iemMemFetchSysU64(PIEMCPU pIemCpu, uint64_t *pu64Dst, uint8_t iSegReg, RTGCPTR GCPtrMem);
 static VBOXSTRICTRC     iemMemFetchSelDesc(PIEMCPU pIemCpu, PIEMSELDESC pDesc, uint16_t uSel);
 static VBOXSTRICTRC     iemMemStackPushCommitSpecial(PIEMCPU pIemCpu, void *pvMem, uint64_t uNewRsp);
@@ -1536,5 +1539,5 @@
 
             uint32_t u32Tmp;
-            rcStrict = iemMemFetchDataU32(pIemCpu, &u32Tmp, UINT8_MAX, pCtx->trHid.u64Base + off);
+            rcStrict = iemMemFetchSysU32(pIemCpu, &u32Tmp, UINT8_MAX, pCtx->trHid.u64Base + off);
             if (rcStrict == VINF_SUCCESS)
             {
@@ -1560,5 +1563,5 @@
 
             uint64_t u64Tmp;
-            rcStrict = iemMemFetchDataU64(pIemCpu, &u64Tmp, UINT8_MAX, pCtx->trHid.u64Base + off);
+            rcStrict = iemMemFetchSysU64(pIemCpu, &u64Tmp, UINT8_MAX, pCtx->trHid.u64Base + off);
             if (rcStrict == VINF_SUCCESS)
             {
@@ -1701,6 +1704,6 @@
     }
     X86DESC Idte;
-    VBOXSTRICTRC rcStrict = iemMemFetchDataU64(pIemCpu, &Idte.u, UINT8_MAX,
-                                               pCtx->idtr.pIdt + UINT32_C(8) * u8Vector);
+    VBOXSTRICTRC rcStrict = iemMemFetchSysU64(pIemCpu, &Idte.u, UINT8_MAX,
+                                              pCtx->idtr.pIdt + UINT32_C(8) * u8Vector);
     if (RT_UNLIKELY(rcStrict != VINF_SUCCESS))
         return rcStrict;
@@ -1871,5 +1874,5 @@
         RTPTRUNION uStackFrame;
         rcStrict = iemMemMap(pIemCpu, &uStackFrame.pv, cbStackFrame, UINT8_MAX,
-                             uNewEsp - cbStackFrame + X86DESC_BASE(DescSS.Legacy), IEM_ACCESS_STACK_W);
+                             uNewEsp - cbStackFrame + X86DESC_BASE(DescSS.Legacy), IEM_ACCESS_STACK_W | IEM_ACCESS_WHAT_SYS); /* _SYS is a hack ... */
         if (rcStrict != VINF_SUCCESS)
             return rcStrict;
@@ -1883,5 +1886,5 @@
         uStackFrame.pu32[3] = pCtx->esp;
         uStackFrame.pu32[4] = pCtx->ss;
-        rcStrict = iemMemCommitAndUnmap(pIemCpu, pvStackFrame, IEM_ACCESS_STACK_W);
+        rcStrict = iemMemCommitAndUnmap(pIemCpu, pvStackFrame, IEM_ACCESS_STACK_W | IEM_ACCESS_WHAT_SYS);
         if (rcStrict != VINF_SUCCESS)
             return rcStrict;
@@ -2067,5 +2070,5 @@
 
         /** @todo double and tripple faults. */
-        AssertReturn(pIemCpu->cXcptRecursions < 3, VERR_NOT_IMPLEMENTED);
+        AssertReturn(pIemCpu->cXcptRecursions < 3, VERR_IEM_ASPECT_NOT_IMPLEMENTED);
 
         /** @todo set X86_TRAP_ERR_EXTERNAL when appropriate.
@@ -2217,4 +2220,12 @@
 
 
+/** \#SS(seg) - 0c.  */
+DECL_NO_INLINE(static, VBOXSTRICTRC) iemRaiseStackSelectorNotPresentBySelector(PIEMCPU pIemCpu, uint16_t uSel)
+{
+    return iemRaiseXcptOrInt(pIemCpu, 0, X86_XCPT_SS, IEM_XCPT_FLAGS_T_CPU_XCPT | IEM_XCPT_FLAGS_ERR,
+                             uSel & ~X86_SEL_RPL, 0);
+}
+
+
 /** \#GP(n) - 0d.  */
 DECL_NO_INLINE(static, VBOXSTRICTRC) iemRaiseGeneralProtectionFault(PIEMCPU pIemCpu, uint16_t uErr)
@@ -2250,5 +2261,6 @@
 {
     NOREF(iSegReg); NOREF(fAccess);
-    return iemRaiseXcptOrInt(pIemCpu, 0, X86_XCPT_GP, IEM_XCPT_FLAGS_T_CPU_XCPT | IEM_XCPT_FLAGS_ERR, 0, 0);
+    return iemRaiseXcptOrInt(pIemCpu, 0, iSegReg == X86_SREG_SS ? X86_XCPT_SS : X86_XCPT_GP,
+                             IEM_XCPT_FLAGS_T_CPU_XCPT | IEM_XCPT_FLAGS_ERR, 0, 0);
 }
 
@@ -3362,19 +3374,38 @@
     }
 
-    if (    (fFlags & (X86_PTE_RW | X86_PTE_US | X86_PTE_PAE_NX)) != (X86_PTE_RW | X86_PTE_US)
-        &&  (   (   (fAccess & IEM_ACCESS_TYPE_WRITE) /* Write to read only memory? */
-                 && !(fFlags & X86_PTE_RW)
-                 && (   pIemCpu->uCpl != 0
-                     || (pIemCpu->CTX_SUFF(pCtx)->cr0 & X86_CR0_WP)) )
-             || (   !(fFlags & X86_PTE_US)            /* Kernel memory */
-                 &&  pIemCpu->uCpl == 3)
-             || (   (fAccess & IEM_ACCESS_TYPE_EXEC)  /* Executing non-executable memory? */
-                 && (fFlags & X86_PTE_PAE_NX)
-                 && (pIemCpu->CTX_SUFF(pCtx)->msrEFER & MSR_K6_EFER_NXE) )
-            )
-       )
-    {
-        *pGCPhysMem = NIL_RTGCPHYS;
-        return iemRaisePageFault(pIemCpu, GCPtrMem, fAccess, VERR_ACCESS_DENIED);
+    /* If the page is writable and does not have the no-exec bit set, all
+       access is allowed.  Otherwise we'll have to check more carefully... */
+    if ((fFlags & (X86_PTE_RW | X86_PTE_US | X86_PTE_PAE_NX)) != (X86_PTE_RW | X86_PTE_US))
+    {
+        /* Write to read only memory? */
+        if (   (fAccess & IEM_ACCESS_TYPE_WRITE)
+            && !(fFlags & X86_PTE_RW)
+            && (   pIemCpu->uCpl != 0
+                || (pIemCpu->CTX_SUFF(pCtx)->cr0 & X86_CR0_WP)))
+        {
+            Log(("iemMemPageTranslateAndCheckAccess: GCPtrMem=%RGv - read-only page\n", GCPtrMem));
+            *pGCPhysMem = NIL_RTGCPHYS;
+            return iemRaisePageFault(pIemCpu, GCPtrMem, fAccess, VERR_ACCESS_DENIED);
+        }
+
+        /* Kernel memory accessed by userland? */
+        if (   !(fFlags & X86_PTE_US)
+            && pIemCpu->uCpl == 3
+            && !(fAccess & IEM_ACCESS_WHAT_SYS))
+        {
+            Log(("iemMemPageTranslateAndCheckAccess: GCPtrMem=%RGv - user access to kernel page\n", GCPtrMem));
+            *pGCPhysMem = NIL_RTGCPHYS;
+            return iemRaisePageFault(pIemCpu, GCPtrMem, fAccess, VERR_ACCESS_DENIED);
+        }
+
+        /* Executing non-executable memory? */
+        if (   (fAccess & IEM_ACCESS_TYPE_EXEC)
+            && (fFlags & X86_PTE_PAE_NX)
+            && (pIemCpu->CTX_SUFF(pCtx)->msrEFER & MSR_K6_EFER_NXE) )
+        {
+            Log(("iemMemPageTranslateAndCheckAccess: GCPtrMem=%RGv - NX\n", GCPtrMem));
+            *pGCPhysMem = NIL_RTGCPHYS;
+            return iemRaisePageFault(pIemCpu, GCPtrMem, fAccess, VERR_ACCESS_DENIED);
+        }
     }
 
@@ -4554,5 +4585,5 @@
  * Begin a special stack pop (used by iret, retf and such).
  *
- * This will raise #SS or #PF if appropriate.
+ * This will raise \#SS or \#PF if appropriate.
  *
  * @returns Strict VBox status code.
@@ -4562,5 +4593,6 @@
  * @param   puNewRsp            Where to return the new RSP value.  This must be
  *                              passed unchanged to
- *                              iemMemStackPopCommitSpecial().
+ *                              iemMemStackPopCommitSpecial() or applied
+ *                              manually if iemMemStackPopDoneSpecial() is used.
  */
 static VBOXSTRICTRC iemMemStackPopBeginSpecial(PIEMCPU pIemCpu, size_t cbMem, void const **ppvMem, uint64_t *puNewRsp)
@@ -4569,4 +4601,30 @@
     PCPUMCTX    pCtx     = pIemCpu->CTX_SUFF(pCtx);
     RTGCPTR     GCPtrTop = iemRegGetRspForPop(pCtx, (uint8_t)cbMem, puNewRsp);
+    return iemMemMap(pIemCpu, (void **)ppvMem, cbMem, X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_R);
+}
+
+
+/**
+ * Continue a special stack pop (used by iret).
+ *
+ * This will raise \#SS or \#PF if appropriate.
+ *
+ * @returns Strict VBox status code.
+ * @param   pIemCpu             The IEM per CPU data.
+ * @param   cbMem               The number of bytes to push onto the stack.
+ * @param   ppvMem              Where to return the pointer to the stack memory.
+ * @param   puNewRsp            Where to return the new RSP value.  This must be
+ *                              passed unchanged to
+ *                              iemMemStackPopCommitSpecial() or applied
+ *                              manually if iemMemStackPopDoneSpecial() is used.
+ */
+static VBOXSTRICTRC iemMemStackPopContinueSpecial(PIEMCPU pIemCpu, size_t cbMem, void const **ppvMem, uint64_t *puNewRsp)
+{
+    Assert(cbMem < UINT8_MAX);
+    PCPUMCTX    pCtx     = pIemCpu->CTX_SUFF(pCtx);
+    RTUINT64U   NewRsp;
+    NewRsp.u = *puNewRsp;
+    RTGCPTR     GCPtrTop = iemRegGetRspForPopEx(&NewRsp, 8, pCtx);
+    *puNewRsp = NewRsp.u;
     return iemMemMap(pIemCpu, (void **)ppvMem, cbMem, X86_SREG_SS, GCPtrTop, IEM_ACCESS_STACK_R);
 }
@@ -4591,4 +4649,70 @@
         pIemCpu->CTX_SUFF(pCtx)->rsp = uNewRsp;
     return rcStrict;
+}
+
+
+/**
+ * Done with a special stack pop (started by iemMemStackPopBeginSpecial or
+ * iemMemStackPopContinueSpecial).
+ *
+ * The caller will manually commit the rSP.
+ *
+ * @returns Strict VBox status code.
+ * @param   pIemCpu             The IEM per CPU data.
+ * @param   pvMem               The pointer returned by
+ *                              iemMemStackPopBeginSpecial() or
+ *                              iemMemStackPopContinueSpecial().
+ */
+static VBOXSTRICTRC iemMemStackPopDoneSpecial(PIEMCPU pIemCpu, void const *pvMem)
+{
+    return iemMemCommitAndUnmap(pIemCpu, (void *)pvMem, IEM_ACCESS_STACK_R);
+}
+
+
+/**
+ * Fetches a system table dword.
+ *
+ * @returns Strict VBox status code.
+ * @param   pIemCpu             The IEM per CPU data.
+ * @param   pu32Dst             Where to return the dword.
+ * @param   iSegReg             The index of the segment register to use for
+ *                              this access.  The base and limits are checked.
+ * @param   GCPtrMem            The address of the guest memory.
+ */
+static VBOXSTRICTRC iemMemFetchSysU32(PIEMCPU pIemCpu, uint32_t *pu32Dst, uint8_t iSegReg, RTGCPTR GCPtrMem)
+{
+    /* The lazy approach for now... */
+    uint32_t const *pu32Src;
+    VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu32Src, sizeof(*pu32Src), iSegReg, GCPtrMem, IEM_ACCESS_SYS_R);
+    if (rc == VINF_SUCCESS)
+    {
+        *pu32Dst = *pu32Src;
+        rc = iemMemCommitAndUnmap(pIemCpu, (void *)pu32Src, IEM_ACCESS_SYS_R);
+    }
+    return rc;
+}
+
+
+/**
+ * Fetches a system table qword.
+ *
+ * @returns Strict VBox status code.
+ * @param   pIemCpu             The IEM per CPU data.
+ * @param   pu64Dst             Where to return the qword.
+ * @param   iSegReg             The index of the segment register to use for
+ *                              this access.  The base and limits are checked.
+ * @param   GCPtrMem            The address of the guest memory.
+ */
+static VBOXSTRICTRC iemMemFetchSysU64(PIEMCPU pIemCpu, uint64_t *pu64Dst, uint8_t iSegReg, RTGCPTR GCPtrMem)
+{
+    /* The lazy approach for now... */
+    uint64_t const *pu64Src;
+    VBOXSTRICTRC rc = iemMemMap(pIemCpu, (void **)&pu64Src, sizeof(*pu64Src), iSegReg, GCPtrMem, IEM_ACCESS_SYS_R);
+    if (rc == VINF_SUCCESS)
+    {
+        *pu64Dst = *pu64Src;
+        rc = iemMemCommitAndUnmap(pIemCpu, (void *)pu64Src, IEM_ACCESS_SYS_R);
+    }
+    return rc;
 }
 
@@ -4640,5 +4764,5 @@
      * required.
      */
-    VBOXSTRICTRC rcStrict = iemMemFetchDataU64(pIemCpu, &pDesc->Legacy.u, UINT8_MAX, GCPtrBase + (uSel & X86_SEL_MASK));
+    VBOXSTRICTRC rcStrict = iemMemFetchSysU64(pIemCpu, &pDesc->Legacy.u, UINT8_MAX, GCPtrBase + (uSel & X86_SEL_MASK));
     if (rcStrict == VINF_SUCCESS)
     {
@@ -4647,5 +4771,5 @@
             pDesc->Long.au64[1] = 0;
         else if ((uint32_t)(uSel & X86_SEL_MASK) + 15 < (uSel & X86_SEL_LDT ? pCtx->ldtrHid.u32Limit : pCtx->gdtr.cbGdt))
-            rcStrict = iemMemFetchDataU64(pIemCpu, &pDesc->Legacy.u, UINT8_MAX, GCPtrBase + (uSel & X86_SEL_MASK));
+            rcStrict = iemMemFetchSysU64(pIemCpu, &pDesc->Legacy.u, UINT8_MAX, GCPtrBase + (uSel & X86_SEL_MASK));
         else
         {
@@ -4692,5 +4816,5 @@
         /* The normal case, map the 32-bit bits around the accessed bit (40). */
         GCPtr += 2 + 2;
-        rcStrict = iemMemMap(pIemCpu, (void **)&pu32, 4, UINT8_MAX, GCPtr, IEM_ACCESS_DATA_RW);
+        rcStrict = iemMemMap(pIemCpu, (void **)&pu32, 4, UINT8_MAX, GCPtr, IEM_ACCESS_SYS_RW);
         if (rcStrict != VINF_SUCCESS)
             return rcStrict;
@@ -4700,5 +4824,5 @@
     {
         /* The misaligned GDT/LDT case, map the whole thing. */
-        rcStrict = iemMemMap(pIemCpu, (void **)&pu32, 8, UINT8_MAX, GCPtr, IEM_ACCESS_DATA_RW);
+        rcStrict = iemMemMap(pIemCpu, (void **)&pu32, 8, UINT8_MAX, GCPtr, IEM_ACCESS_SYS_RW);
         if (rcStrict != VINF_SUCCESS)
             return rcStrict;
@@ -6219,4 +6343,6 @@
         if (cDiffs != 0)
         {
+            if (LogIs3Enabled())
+                DBGFR3Info(pVM, "cpumguest", "verbose", NULL);
             RTAssertMsg1(NULL, __LINE__, __FILE__, __FUNCTION__);
             iemVerifyAssertMsg2(pIemCpu);
@@ -6369,4 +6495,7 @@
               (RTSEL)pCtx->fs, (RTSEL)pCtx->gs, pCtx->eflags.u,
               szInstr));
+
+        if (LogIs3Enabled())
+            DBGFR3Info(pVCpu->pVMR3, "cpumguest", "verbose", NULL);
     }
 #endif
Index: trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h
===================================================================
--- trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h	(revision 39931)
+++ trunk/src/VBox/VMM/VMMAll/IEMAllCImpl.cpp.h	(revision 39945)
@@ -99,4 +99,39 @@
 
 
+/**
+ * Loads a NULL data selector into a selector register, both the hidden and
+ * visible parts, in protected mode.
+ *
+ * @param   puSel               The selector register.
+ * @param   pHid                The hidden register part.
+ */
+static void iemHlpLoadNullDataSelectorProt(PRTSEL puSel, PCPUMSELREGHID pHid)
+{
+    /** @todo write a testcase checking what happends when loading a NULL data
+     *        selector in protected mode. */
+    pHid->u64Base  = 0;
+    pHid->u32Limit = 0;
+    pHid->Attr.u   = 0;
+    *puSel = 0;
+}
+
+
+/**
+ * Helper used by iret.
+ *
+ * @param   uCpl                The new CPL.
+ * @param   puSel               The selector register.
+ * @param   pHid                The corresponding hidden register.
+ */
+static void iemHlpAdjustSelectorForNewCpl(uint8_t uCpl, PRTSEL puSel, PCPUMSELREGHID pHid)
+{
+    if (   uCpl > pHid->Attr.n.u2Dpl
+        && pHid->Attr.n.u1DescType /* code or data, not system */
+        &&    (pHid->Attr.n.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF))
+           !=                        (X86_SEL_TYPE_CODE | X86_SEL_TYPE_CONF)) /* not conforming code */
+        iemHlpLoadNullDataSelectorProt(puSel, pHid);
+}
+
+
 /** @} */
 
@@ -1378,5 +1413,8 @@
             rcStrict = iemMemFetchSelDesc(pIemCpu, &DescCS, uNewCS);
             if (rcStrict != VINF_SUCCESS)
+            {
+                Log(("iret %04x:%08x - rcStrict=%Rrc when fetching CS\n", uNewCS, uNewEip, VBOXSTRICTRC_VAL(rcStrict)));
                 return rcStrict;
+            }
 
             /* Must be a code descriptor. */
@@ -1417,12 +1455,144 @@
 
             /*
-             * Different level?
+             * Return to outer level?
              */
             if ((uNewCS & X86_SEL_RPL) != pIemCpu->uCpl)
             {
-                AssertFailedReturn(VERR_NOT_IMPLEMENTED);
+                uint16_t    uNewSS;
+                uint32_t    uNewESP;
+                if (enmEffOpSize == IEMMODE_32BIT)
+                {
+                    rcStrict = iemMemStackPopContinueSpecial(pIemCpu, 8, &uFrame.pv, &uNewRsp);
+                    if (rcStrict != VINF_SUCCESS)
+                        return rcStrict;
+                    uNewESP = uFrame.pu32[0];
+                    uNewSS  = (uint16_t)uFrame.pu32[1];
+                }
+                else
+                {
+                    rcStrict = iemMemStackPopContinueSpecial(pIemCpu, 8, &uFrame.pv, &uNewRsp);
+                    if (rcStrict != VINF_SUCCESS)
+                        return rcStrict;
+                    uNewESP = uFrame.pu16[0];
+                    uNewSS  = uFrame.pu16[1];
+                }
+                rcStrict = iemMemCommitAndUnmap(pIemCpu, (void *)uFrame.pv, IEM_ACCESS_STACK_R);
+                if (rcStrict != VINF_SUCCESS)
+                    return rcStrict;
+
+                /* Read the SS descriptor. */
+                if (!(uNewSS & (X86_SEL_MASK | X86_SEL_LDT)))
+                {
+                    Log(("iret %04x:%08x/%04x:%08x -> invalid SS selector, #GP(0)\n", uNewCS, uNewEip, uNewSS, uNewESP));
+                    return iemRaiseGeneralProtectionFault0(pIemCpu);
+                }
+
+                IEMSELDESC DescSS;
+                rcStrict = iemMemFetchSelDesc(pIemCpu, &DescSS, uNewSS);
+                if (rcStrict != VINF_SUCCESS)
+                {
+                    Log(("iret %04x:%08x/%04x:%08x - %Rrc when fetching SS\n",
+                         uNewCS, uNewEip, uNewSS, uNewESP, VBOXSTRICTRC_VAL(rcStrict)));
+                    return rcStrict;
+                }
+
+                /* Privilege checks. */
+                if ((uNewSS & X86_SEL_RPL) != (uNewCS & X86_SEL_RPL))
+                {
+                    Log(("iret %04x:%08x/%04x:%08x -> SS.RPL != CS.RPL -> #GP\n", uNewCS, uNewEip, uNewSS, uNewESP));
+                    return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSS);
+                }
+                if (DescSS.Legacy.Gen.u2Dpl != (uNewCS & X86_SEL_RPL))
+                {
+                    Log(("iret %04x:%08x/%04x:%08x -> SS.DPL (%d) != CS.RPL -> #GP\n",
+                         uNewCS, uNewEip, uNewSS, uNewESP, DescSS.Legacy.Gen.u2Dpl));
+                    return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSS);
+                }
+
+                /* Must be a writeable data segment descriptor. */
+                if (!DescSS.Legacy.Gen.u1DescType)
+                {
+                    Log(("iret %04x:%08x/%04x:%08x -> SS is system segment (%#x) -> #GP\n",
+                         uNewCS, uNewEip, uNewSS, uNewESP, DescSS.Legacy.Gen.u4Type));
+                    return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSS);
+                }
+                if ((DescSS.Legacy.Gen.u4Type & (X86_SEL_TYPE_CODE | X86_SEL_TYPE_WRITE)) != X86_SEL_TYPE_WRITE)
+                {
+                    Log(("iret %04x:%08x/%04x:%08x - not writable data segment (%#x) -> #GP\n",
+                         uNewCS, uNewEip, uNewSS, uNewESP, DescSS.Legacy.Gen.u4Type));
+                    return iemRaiseGeneralProtectionFaultBySelector(pIemCpu, uNewSS);
+                }
+
+                /* Present? */
+                if (!DescSS.Legacy.Gen.u1Present)
+                {
+                    Log(("iret %04x:%08x/%04x:%08x -> SS not present -> #SS\n", uNewCS, uNewEip, uNewSS, uNewESP));
+                    return iemRaiseStackSelectorNotPresentBySelector(pIemCpu, uNewSS);
+                }
+
+                uint32_t cbLimitSS = X86DESC_LIMIT(DescSS.Legacy);
+                if (DescSS.Legacy.Gen.u1Granularity)
+                    cbLimitSS = (cbLimitSS << PAGE_SHIFT) | PAGE_OFFSET_MASK;
+
+                /* Check EIP. */
+                if (uNewEip > cbLimitCS)
+                {
+                    Log(("iret %04x:%08x/%04x:%08x -> EIP is out of bounds (%#x) -> #GP(0)\n",
+                         uNewCS, uNewEip, uNewSS, uNewESP, cbLimitCS));
+                    return iemRaiseSelectorBoundsBySelector(pIemCpu, uNewCS);
+                }
+
+                /*
+                 * Commit the changes, marking CS and SS accessed first since
+                 * that may fail.
+                 */
+                if (!(DescCS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+                {
+                    rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewCS);
+                    if (rcStrict != VINF_SUCCESS)
+                        return rcStrict;
+                    DescCS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
+                }
+                if (!(DescSS.Legacy.Gen.u4Type & X86_SEL_TYPE_ACCESSED))
+                {
+                    rcStrict = iemMemMarkSelDescAccessed(pIemCpu, uNewSS);
+                    if (rcStrict != VINF_SUCCESS)
+                        return rcStrict;
+                    DescSS.Legacy.Gen.u4Type |= X86_SEL_TYPE_ACCESSED;
+                }
+
+                pCtx->rip               = uNewEip;
+                pCtx->cs                = uNewCS;
+                pCtx->csHid.Attr.u      = X86DESC_GET_HID_ATTR(DescCS.Legacy);
+                pCtx->csHid.u32Limit    = cbLimitCS;
+                pCtx->csHid.u64Base     = X86DESC_BASE(DescCS.Legacy);
+                pCtx->rsp               = uNewESP;
+                pCtx->ss                = uNewSS;
+                pCtx->ssHid.Attr.u      = X86DESC_GET_HID_ATTR(DescSS.Legacy);
+                pCtx->ssHid.u32Limit    = cbLimitSS;
+                pCtx->ssHid.u64Base     = X86DESC_BASE(DescSS.Legacy);
+
+                uint32_t fEFlagsMask = X86_EFL_CF | X86_EFL_PF | X86_EFL_AF | X86_EFL_ZF  | X86_EFL_SF
+                                     | X86_EFL_TF | X86_EFL_DF | X86_EFL_OF | X86_EFL_NT;
+                if (enmEffOpSize != IEMMODE_16BIT)
+                    fEFlagsMask |= X86_EFL_RF | X86_EFL_AC | X86_EFL_ID;
+                if (pIemCpu->uCpl == 0)
+                    fEFlagsMask |= X86_EFL_IF | X86_EFL_IOPL | X86_EFL_VIF | X86_EFL_VIP; /* VM is 0 */
+                else if (pIemCpu->uCpl <= pCtx->eflags.Bits.u2IOPL)
+                    fEFlagsMask |= X86_EFL_IF;
+                pCtx->eflags.u         &= ~fEFlagsMask;
+                pCtx->eflags.u         |= fEFlagsMask & uNewFlags;
+
+                pIemCpu->uCpl           = uNewCS & X86_SEL_RPL;
+                iemHlpAdjustSelectorForNewCpl(uNewCS & X86_SEL_RPL, &pCtx->ds, &pCtx->dsHid);
+                iemHlpAdjustSelectorForNewCpl(uNewCS & X86_SEL_RPL, &pCtx->es, &pCtx->esHid);
+                iemHlpAdjustSelectorForNewCpl(uNewCS & X86_SEL_RPL, &pCtx->fs, &pCtx->fsHid);
+                iemHlpAdjustSelectorForNewCpl(uNewCS & X86_SEL_RPL, &pCtx->gs, &pCtx->gsHid);
+
+                /* Done! */
+
             }
             /*
-             * Same level.
+             * Return to the same level.
              */
             else
