Index: trunk/src/VBox/VMM/SELM.cpp
===================================================================
--- trunk/src/VBox/VMM/SELM.cpp	(revision 17105)
+++ trunk/src/VBox/VMM/SELM.cpp	(revision 17106)
@@ -1474,5 +1474,5 @@
         Assert(   trHid.Attr.n.u4Type == X86_SEL_TYPE_SYS_286_TSS_BUSY
                || trHid.Attr.n.u4Type == X86_SEL_TYPE_SYS_386_TSS_BUSY);
-        if (++cbTss)
+        if (!++cbTss)
             cbTss = UINT32_MAX;
     }
@@ -1488,70 +1488,62 @@
 
     /*
-     * Presently we monitor the TSS and the interrupt redirection bitmap if it's present.
-     * We're assuming the guest is playing nice and that the bits we're monitoring won't
-     * cross page boundraries. (The TSS core must be on a single page, while the bitmap
-     * probably doesn't need to be.)
-     */
-    uint32_t cbMonitoredTss = cbTss > sizeof(VBOXTSS) ? sizeof(VBOXTSS) : cbTss;
-
-    /*
-     * We're also completely uninterested in a 16-bit TSS.
-     */
+     * Figure out the size of what need to monitor.
+     */
+    /* We're not interested in any 16-bit TSSes. */
+    uint32_t cbMonitoredTss = cbTss;
     if (    trHid.Attr.n.u4Type != X86_SEL_TYPE_SYS_386_TSS_AVAIL
         &&  trHid.Attr.n.u4Type != X86_SEL_TYPE_SYS_386_TSS_BUSY)
         cbMonitoredTss = 0;
-    AssertMsg((GCPtrTss >> PAGE_SHIFT) == ((GCPtrTss + cbMonitoredTss - 1) >> PAGE_SHIFT) || !cbMonitoredTss,
-              ("GCPtrTss=%RGv cbMonitoredTss=%#x - We assume everything is inside one page!\n", GCPtrTss, cbMonitoredTss));
-
-    /*
-     * Check for monitor changes and apply them.
-     */
-    if (    GCPtrTss        != pVM->selm.s.GCPtrGuestTss
-        ||  cbMonitoredTss  != pVM->selm.s.cbMonitoredGuestTss)
-    {
-        Log(("SELMR3SyncTSS: Guest's TSS is changed to pTss=%RGv cbMonitoredTss=%08X cbGuestTss=%#08x\n",
-             GCPtrTss, cbMonitoredTss, pVM->selm.s.cbGuestTss));
-
-        /* Release the old range first. */
-        if (pVM->selm.s.GCPtrGuestTss != RTRCPTR_MAX)
-        {
-            rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestTss);
-            AssertRC(rc);
-        }
-
-        /* Register the write handler if TS != 0. */
-        if (cbMonitoredTss != 0)
-        {
-            rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtrTss, GCPtrTss + cbMonitoredTss - 1,
-                                             0, selmR3GuestTSSWriteHandler,
-                                             "selmRCGuestTSSWriteHandler", 0, "Guest TSS write access handler");
-            if (RT_FAILURE(rc))
-            {
-                STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
-                return rc;
-            }
-
-            /* Update saved Guest TSS info. */
-            pVM->selm.s.GCPtrGuestTss       = GCPtrTss;
-            pVM->selm.s.cbMonitoredGuestTss = cbMonitoredTss;
-            pVM->selm.s.GCSelTss            = SelTss;
-        }
-        else
-        {
-            pVM->selm.s.GCPtrGuestTss       = RTRCPTR_MAX;
-            pVM->selm.s.cbMonitoredGuestTss = 0;
-            pVM->selm.s.GCSelTss            = 0;
-        }
-    }
-
-    /*
-     * Update the ring 0 stack selector and base address.
-     * (Reading up to and including offIoBitmap to save effort in the VME case.)
-     */
+
+    pVM->selm.s.offGuestIoBitmap = 0;
     bool fNoRing1Stack = true;
     if (cbMonitoredTss)
     {
+        /*
+         * 32-bit TSS. What we're really keen on is the SS0 and ESP0 fields.
+         * If VME is enabled we also want to keep an eye on the interrupt
+         * redirection bitmap.
+         */
         VBOXTSS Tss;
-        rc = PGMPhysSimpleReadGCPtr(pVM, &Tss, GCPtrTss, RT_OFFSETOF(VBOXTSS, offIoBitmap) + sizeof(Tss.offIoBitmap));
+        uint32_t cr4 = CPUMGetGuestCR4(pVM);
+        rc = PGMPhysSimpleReadGCPtr(pVM, &Tss, GCPtrTss, RT_OFFSETOF(VBOXTSS, IntRedirBitmap));
+        if (    !(cr4 & X86_CR4_VME)
+            ||   (  VBOX_SUCCESS(rc)
+                 && Tss.offIoBitmap < sizeof(VBOXTSS) /* too small */
+                 && Tss.offIoBitmap > cbTss)          /* beyond the end */ /** @todo not sure how the partial case is handled; probably not allowed. */
+           )
+            /* No interrupt redirection bitmap, just ESP0 and SS0. */
+            cbMonitoredTss = RT_UOFFSETOF(VBOXTSS, padding_ss0);
+        else if (RT_SUCCESS(rc))
+        {
+            /*
+             * Everything up to and including the interrupt redirection bitmap. Unfortunately
+             * this can be quite a large chunk. We use to skip it earlier and just hope it
+             * was kind of static...
+             *
+             * Update the virtual interrupt redirection bitmap while we're here.
+             * (It is located in the 32 bytes before TR:offIoBitmap.)
+             */
+            cbMonitoredTss = Tss.offIoBitmap;
+            pVM->selm.s.offGuestIoBitmap = Tss.offIoBitmap;
+
+            uint32_t offRedirBitmap = Tss.offIoBitmap - sizeof(Tss.IntRedirBitmap);
+            rc = PGMPhysSimpleReadGCPtr(pVM, &pVM->selm.s.Tss.IntRedirBitmap,
+                                        GCPtrTss + offRedirBitmap, sizeof(Tss.IntRedirBitmap));
+            AssertRC(rc);
+            /** @todo memset the bitmap on failure? */
+            Log2(("Redirection bitmap:\n"));
+            Log2(("%.*Rhxd\n", sizeof(Tss.IntRedirBitmap), &pVM->selm.s.Tss.IntRedirBitmap));
+        }
+        else
+        {
+            cbMonitoredTss = RT_OFFSETOF(VBOXTSS, IntRedirBitmap);
+            pVM->selm.s.offGuestIoBitmap = 0;
+            /** @todo memset the bitmap? */
+        }
+
+        /*
+         * Update the ring 0 stack selector and base address.
+         */
         if (RT_SUCCESS(rc))
         {
@@ -1576,37 +1568,13 @@
             AssertMsg(!(Tss.ss0 & 3), ("ring-1 leak into TSS.SS0? %04X:%08X\n", Tss.ss0, Tss.esp0));
 
-
             /* Update our TSS structure for the guest's ring 1 stack */
             selmSetRing1Stack(pVM, Tss.ss0 | 1, Tss.esp0);
             pVM->selm.s.fSyncTSSRing0Stack = fNoRing1Stack = false;
-
-            /*
-             * Should we sync the virtual interrupt redirection bitmap as well?
-             */
-            if (CPUMGetGuestCR4(pVM) & X86_CR4_VME)
-            {
-                /* Make sure the io bitmap offset is valid; anything less than sizeof(VBOXTSS) means there's none. */
-                if (Tss.offIoBitmap < RT_OFFSETOF(VBOXTSS, IntRedirBitmap) + sizeof(Tss.IntRedirBitmap))
-                {
-                    Log(("Invalid io bitmap offset detected (%x)!\n", Tss.offIoBitmap));
-                    Tss.offIoBitmap = RT_OFFSETOF(VBOXTSS, IntRedirBitmap) + sizeof(Tss.IntRedirBitmap);
-                }
-
-                uint32_t offRedirBitmap = Tss.offIoBitmap - sizeof(Tss.IntRedirBitmap);
-
-                /** @todo not sure how the partial case is handled; probably not allowed */
-                if (offRedirBitmap + sizeof(Tss.IntRedirBitmap) <= pVM->selm.s.cbGuestTss)
-                {
-                    rc = PGMPhysSimpleReadGCPtr(pVM, &pVM->selm.s.Tss.IntRedirBitmap, GCPtrTss + offRedirBitmap, sizeof(Tss.IntRedirBitmap));
-                    AssertRC(rc);
-                    Log2(("Redirection bitmap:\n"));
-                    Log2(("%.*Rhxd\n", sizeof(Tss.IntRedirBitmap), &pVM->selm.s.Tss.IntRedirBitmap));
-                }
-            }
-        }
-    }
-
-    /*
-     * Flush the ring-1 stack and the direct syscall dispatching if we cannot obtain SS0:ESP0.
+        }
+    }
+
+    /*
+     * Flush the ring-1 stack and the direct syscall dispatching if we
+     * cannot obtain SS0:ESP0.
      */
     if (fNoRing1Stack)
@@ -1618,4 +1586,45 @@
         TRPMR3SetGuestTrapHandler(pVM, 0x2E, TRPM_INVALID_HANDLER);
         TRPMR3SetGuestTrapHandler(pVM, 0x80, TRPM_INVALID_HANDLER);
+    }
+
+    /*
+     * Check for monitor changes and apply them.
+     */
+    if (    GCPtrTss        != pVM->selm.s.GCPtrGuestTss
+        ||  cbMonitoredTss  != pVM->selm.s.cbMonitoredGuestTss)
+    {
+        Log(("SELMR3SyncTSS: Guest's TSS is changed to pTss=%RGv cbMonitoredTss=%08X cbGuestTss=%#08x\n",
+             GCPtrTss, cbMonitoredTss, pVM->selm.s.cbGuestTss));
+
+        /* Release the old range first. */
+        if (pVM->selm.s.GCPtrGuestTss != RTRCPTR_MAX)
+        {
+            rc = PGMHandlerVirtualDeregister(pVM, pVM->selm.s.GCPtrGuestTss);
+            AssertRC(rc);
+        }
+
+        /* Register the write handler if TS != 0. */
+        if (cbMonitoredTss != 0)
+        {
+            rc = PGMR3HandlerVirtualRegister(pVM, PGMVIRTHANDLERTYPE_WRITE, GCPtrTss, GCPtrTss + cbMonitoredTss - 1,
+                                             0, selmR3GuestTSSWriteHandler,
+                                             "selmRCGuestTSSWriteHandler", 0, "Guest TSS write access handler");
+            if (RT_FAILURE(rc))
+            {
+                STAM_PROFILE_STOP(&pVM->selm.s.StatUpdateFromCPUM, a);
+                return rc;
+            }
+
+            /* Update saved Guest TSS info. */
+            pVM->selm.s.GCPtrGuestTss       = GCPtrTss;
+            pVM->selm.s.cbMonitoredGuestTss = cbMonitoredTss;
+            pVM->selm.s.GCSelTss            = SelTss;
+        }
+        else
+        {
+            pVM->selm.s.GCPtrGuestTss       = RTRCPTR_MAX;
+            pVM->selm.s.cbMonitoredGuestTss = 0;
+            pVM->selm.s.GCSelTss            = 0;
+        }
     }
 
@@ -1793,5 +1802,5 @@
                      || trHid.Attr.n.u4Type == X86_SEL_TYPE_SYS_386_TSS_BUSY,
                      false);
-        if (++cbTss)
+        if (!++cbTss)
             cbTss = UINT32_MAX;
     }
@@ -1817,47 +1826,82 @@
                     false);
 
-    /*
-     * Cap the TSS size, see SELMR3SyncTSS for details.
-     */
-    uint32_t cbMonitoredTss = cbTss > sizeof(VBOXTSS) ? sizeof(VBOXTSS) : cbTss;
-    AssertMsg((GCPtrTss >> PAGE_SHIFT) == ((GCPtrTss + cbMonitoredTss - 1) >> PAGE_SHIFT) || !cbMonitoredTss,
-              ("GCPtrTss=%RGv cbMonitoredTss=%#x - We assume everything is inside one page!\n", GCPtrTss, cbMonitoredTss));
-    AssertMsgReturn(pVM->selm.s.cbMonitoredGuestTss == cbMonitoredTss, ("%#x %#x\n", pVM->selm.s.cbMonitoredGuestTss, cbMonitoredTss), false);
-
-    /*
-     * Check SS0 and ESP0.
-     */
-    if (    cbMonitoredTss
-        &&  !pVM->selm.s.fSyncTSSRing0Stack)
-    {
-        RTGCPTR     GCPtrGuestTSS = pVM->selm.s.GCPtrGuestTss;
-        uint32_t    ESPR0;
-        int rc = PGMPhysSimpleReadGCPtr(pVM, &ESPR0, GCPtrGuestTSS + RT_OFFSETOF(VBOXTSS, esp0), sizeof(ESPR0));
-        if (RT_SUCCESS(rc))
-        {
-            RTSEL   SelSS0;
-            rc = PGMPhysSimpleReadGCPtr(pVM, &SelSS0, GCPtrGuestTSS + RT_OFFSETOF(VBOXTSS, ss0), sizeof(SelSS0));
+
+    /*
+     * Figure out the size of what need to monitor.
+     */
+    bool fNoRing1Stack = true;
+    /* We're not interested in any 16-bit TSSes. */
+    uint32_t cbMonitoredTss = cbTss;
+    if (    trHid.Attr.n.u4Type != X86_SEL_TYPE_SYS_386_TSS_AVAIL
+        &&  trHid.Attr.n.u4Type != X86_SEL_TYPE_SYS_386_TSS_BUSY)
+        cbMonitoredTss = 0;
+    if (cbMonitoredTss)
+    {
+        VBOXTSS Tss;
+        uint32_t cr4 = CPUMGetGuestCR4(pVM);
+        int rc = PGMPhysSimpleReadGCPtr(pVM, &Tss, GCPtrTss, RT_OFFSETOF(VBOXTSS, IntRedirBitmap));
+        AssertReturn(   rc == VINF_SUCCESS
+                        /* Happends early in XP boot during page table switching. */
+                     || (   (rc == VERR_PAGE_TABLE_NOT_PRESENT || rc == VERR_PAGE_NOT_PRESENT)
+                         && !(CPUMGetGuestEFlags(pVM) & X86_EFL_IF)),
+                     false);
+        if (    !(cr4 & X86_CR4_VME)
+            ||   (  VBOX_SUCCESS(rc)
+                 && Tss.offIoBitmap < sizeof(VBOXTSS) /* too small */
+                 && Tss.offIoBitmap > cbTss)
+           )
+            cbMonitoredTss = RT_UOFFSETOF(VBOXTSS, padding_ss0);
+        else if (RT_SUCCESS(rc))
+        {
+            cbMonitoredTss = Tss.offIoBitmap;
+            AssertMsgReturn(pVM->selm.s.offGuestIoBitmap == Tss.offIoBitmap,
+                            ("#x %#x\n", pVM->selm.s.offGuestIoBitmap, Tss.offIoBitmap),
+                            false);
+
+            /* check the bitmap */
+            uint32_t offRedirBitmap = Tss.offIoBitmap - sizeof(Tss.IntRedirBitmap);
+            rc = PGMPhysSimpleReadGCPtr(pVM, &Tss.IntRedirBitmap,
+                                        GCPtrTss + offRedirBitmap, sizeof(Tss.IntRedirBitmap));
             AssertRCReturn(rc, false);
-
-            if (    ESPR0 != pVM->selm.s.Tss.esp1
-                ||  SelSS0 != (pVM->selm.s.Tss.ss1 & ~1))
+            AssertMsgReturn(!memcmp(&Tss.IntRedirBitmap[0], &pVM->selm.s.Tss.IntRedirBitmap[0], sizeof(Tss.IntRedirBitmap)),
+                            ("offIoBitmap=%#x cbTss=%#x\n"
+                             " Guest: %.32Rhxs\n"
+                             "Shadow: %.32Rhxs\n",
+                             Tss.offIoBitmap, cbTss,
+                             &Tss.IntRedirBitmap[0],
+                             &pVM->selm.s.Tss.IntRedirBitmap[0]),
+                            false);
+        }
+        else
+            cbMonitoredTss = RT_OFFSETOF(VBOXTSS, IntRedirBitmap);
+
+        /*
+         * Check SS0 and ESP0.
+         */
+        if (    !pVM->selm.s.fSyncTSSRing0Stack
+            &&  RT_SUCCESS(rc))
+        {
+            if (    Tss.esp0 != pVM->selm.s.Tss.esp1
+                ||  Tss.ss0  != (pVM->selm.s.Tss.ss1 & ~1))
             {
                 RTGCPHYS GCPhys;
-                rc = PGMGstGetPage(pVM, GCPtrGuestTSS, NULL, &GCPhys); AssertRC(rc);
+                rc = PGMGstGetPage(pVM, GCPtrTss, NULL, &GCPhys); AssertRC(rc);
                 AssertMsgFailed(("TSS out of sync!! (%04X:%08X vs %04X:%08X (guest)) Tss=%RGv Phys=%RGp\n",
-                                 (pVM->selm.s.Tss.ss1 & ~1), pVM->selm.s.Tss.esp1, SelSS0, ESPR0, GCPtrGuestTSS, GCPhys));
+                                 (pVM->selm.s.Tss.ss1 & ~1), pVM->selm.s.Tss.esp1,
+                                 Tss.ss1, Tss.esp1, GCPtrTss, GCPhys));
                 return false;
             }
         }
-        else
-            /* Happens during early Windows XP boot when it is switching page tables. */
-            AssertReturn(rc == VINF_SUCCESS || ((rc == VERR_PAGE_TABLE_NOT_PRESENT || rc == VERR_PAGE_NOT_PRESENT) && !(CPUMGetGuestEFlags(pVM) & X86_EFL_IF)),
-                         false);
-    }
-    else if (!cbMonitoredTss)
+        AssertMsgReturn(pVM->selm.s.cbMonitoredGuestTss == cbMonitoredTss, ("%#x %#x\n", pVM->selm.s.cbMonitoredGuestTss, cbMonitoredTss), false);
+    }
+    else
     {
         AssertMsgReturn(pVM->selm.s.Tss.ss1 == 0 && pVM->selm.s.Tss.esp1 == 0, ("%04x:%08x\n", pVM->selm.s.Tss.ss1, pVM->selm.s.Tss.esp1), false);
         AssertReturn(!pVM->selm.s.fSyncTSSRing0Stack, false);
-    }
+        AssertMsgReturn(pVM->selm.s.cbMonitoredGuestTss == cbMonitoredTss, ("%#x %#x\n", pVM->selm.s.cbMonitoredGuestTss, cbMonitoredTss), false);
+    }
+
+
+
     return true;
 
Index: trunk/src/VBox/VMM/SELMInternal.h
===================================================================
--- trunk/src/VBox/VMM/SELMInternal.h	(revision 17105)
+++ trunk/src/VBox/VMM/SELMInternal.h	(revision 17106)
@@ -140,4 +140,7 @@
      * Contains RTSEL_MAX if not set. */
     RTSEL                   GCSelTss;
+    /** The last known offset of the I/O bitmap.
+     * This is only used if we monitor the bitmap. */
+    uint16_t                offGuestIoBitmap;
 
     /** Indicates that the Guest GDT access handler have been registered. */
@@ -149,6 +152,5 @@
     /** Indicates whether the TSS stack selector & base address need to be refreshed.  */
     bool                    fSyncTSSRing0Stack;
-    /** alignment . */
-    RTUINT                  uPadding2;
+    bool                    fPadding2[1+2];
 
     /** SELMR3UpdateFromCPUM() profiling. */
Index: trunk/src/VBox/VMM/VMMAll/CPUMAllRegs.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/CPUMAllRegs.cpp	(revision 17105)
+++ trunk/src/VBox/VMM/VMMAll/CPUMAllRegs.cpp	(revision 17106)
@@ -640,5 +640,5 @@
     PCPUMCPU pCpumCpu = cpumGetCpumCpu(pVM);
 
-    if (    (cr4                   & (X86_CR4_PGE | X86_CR4_PAE | X86_CR4_PSE))
+    if (    (cr4                 & (X86_CR4_PGE | X86_CR4_PAE | X86_CR4_PSE))
         !=  (pCpumCpu->Guest.cr4 & (X86_CR4_PGE | X86_CR4_PAE | X86_CR4_PSE)))
         pCpumCpu->fChanged |= CPUM_CHANGED_GLOBAL_TLB_FLUSH;
Index: trunk/src/VBox/VMM/VMMAll/EMAll.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMAll/EMAll.cpp	(revision 17105)
+++ trunk/src/VBox/VMM/VMMAll/EMAll.cpp	(revision 17106)
@@ -2094,13 +2094,13 @@
         oldval = CPUMGetGuestCR4(pVM);
         rc = CPUMSetGuestCR4(pVM, val); AssertRC(rc);
-        val   = CPUMGetGuestCR4(pVM);
-
+        val = CPUMGetGuestCR4(pVM);
+
+        /* Illegal to disable PAE when long mode is active. (AMD Arch. Programmer's Manual Volume 2: Table 14-5) */
         msrEFER = CPUMGetGuestEFER(pVM);
-        /* Illegal to disable PAE when long mode is active. (AMD Arch. Programmer's Manual Volume 2: Table 14-5) */
         if (    (msrEFER & MSR_K6_EFER_LMA)
             &&  (oldval & X86_CR4_PAE)
             &&  !(val & X86_CR4_PAE))
         {
-            return VERR_EM_INTERPRETER; /* @todo generate #GP(0) */
+            return VERR_EM_INTERPRETER; /** @todo generate #GP(0) */
         }
 
@@ -2112,6 +2112,7 @@
             AssertRCReturn(rc, rc);
         }
+
+        /* Feeling extremely lazy. */
 # ifdef IN_RC
-        /* Feeling extremely lazy. */
         if (    (oldval & (X86_CR4_OSFSXR|X86_CR4_OSXMMEEXCPT|X86_CR4_PCE|X86_CR4_MCE|X86_CR4_PAE|X86_CR4_DE|X86_CR4_TSD|X86_CR4_PVI|X86_CR4_VME))
             !=  (val    & (X86_CR4_OSFSXR|X86_CR4_OSXMMEEXCPT|X86_CR4_PCE|X86_CR4_MCE|X86_CR4_PAE|X86_CR4_DE|X86_CR4_TSD|X86_CR4_PVI|X86_CR4_VME)))
@@ -2121,4 +2122,7 @@
         }
 # endif
+        if ((val ^ oldval) & X86_CR4_VME)
+            VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
+
         return PGMChangeMode(pVM, CPUMGetGuestCR0(pVM), CPUMGetGuestCR4(pVM), CPUMGetGuestEFER(pVM));
 
Index: trunk/src/VBox/VMM/VMMGC/SELMGC.cpp
===================================================================
--- trunk/src/VBox/VMM/VMMGC/SELMGC.cpp	(revision 17105)
+++ trunk/src/VBox/VMM/VMMGC/SELMGC.cpp	(revision 17106)
@@ -255,4 +255,29 @@
 
 /**
+ * Read wrapper used by selmRCGuestTSSWriteHandler.
+ * @returns VBox status code (appropriate for trap handling and GC return).
+ * @param   pVM         The VM handle
+ * @param   pvDst       Where to put the bits we read.
+ * @param   pvSrc       Guest address to read from.
+ * @param   cb          The number of bytes to read.
+ */
+DECLINLINE(int) selmRCReadTssBits(PVM pVM, void *pvDst, void const *pvSrc, size_t cb)
+{
+    int rc = MMGCRamRead(pVM, pvDst, (void *)pvSrc, cb);
+    if (RT_SUCCESS(rc))
+        return VINF_SUCCESS;
+
+    /** @todo use different fallback?    */
+    rc = PGMPrefetchPage(pVM, (uintptr_t)pvSrc);
+    AssertMsg(rc == VINF_SUCCESS, ("PGMPrefetchPage %p failed with %Rrc\n", &pvSrc, rc));
+    if (rc == VINF_SUCCESS)
+    {
+        rc = MMGCRamRead(pVM, pvDst, (void *)pvSrc, cb);
+        AssertMsg(rc == VINF_SUCCESS, ("MMGCRamRead %p failed with %Rrc\n", &pvSrc, rc));
+    }
+    return rc;
+}
+
+/**
  * \#PF Virtual Handler callback for Guest write access to the Guest's own current TSS.
  *
@@ -271,10 +296,5 @@
 
     /*
-     * Try emulate the access and compare the R0 ss:esp with the shadow tss values.
-     *
-     * Note, that it's safe to access the TSS after a successfull instruction emulation,
-     * even if the stuff that was changed wasn't the ss0 or esp0 bits. The CPU insists
-     * on the TSS being all one physical page, so ASSUMING that we're not trapping
-     * I/O map accesses this is safe.
+     * Try emulate the access.
      */
     uint32_t cb;
@@ -282,44 +302,100 @@
     if (RT_SUCCESS(rc) && cb)
     {
-        PCVBOXTSS pGuestTSS = (PVBOXTSS)pVM->selm.s.GCPtrGuestTss;
-        if (    pGuestTSS->esp0 !=  pVM->selm.s.Tss.esp1
-            ||  pGuestTSS->ss0  != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
+        rc = VINF_SUCCESS;
+
+        /*
+         * If it's on the same page as the esp0 and ss0 fields or actually one of them,
+         * then check if any of these has changed.
+         */
+        PCVBOXTSS pGuestTss = (PVBOXTSS)pVM->selm.s.GCPtrGuestTss;
+        if (    PAGE_ADDRESS(&pGuestTss->esp0) == PAGE_ADDRESS(&pGuestTss->padding_ss0)
+            &&  PAGE_ADDRESS(&pGuestTss->esp0) == PAGE_ADDRESS((uint8_t *)pGuestTss + offRange)
+            &&  (    pGuestTss->esp0 !=  pVM->selm.s.Tss.esp1
+                 ||  pGuestTss->ss0  != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
+           )
         {
             Log(("selmRCGuestTSSWriteHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv\n",
-                 (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)pGuestTSS->ss0, (RTGCPTR)pGuestTSS->esp0));
-            pVM->selm.s.Tss.esp1 = pGuestTSS->esp0;
-            pVM->selm.s.Tss.ss1 = pGuestTSS->ss0 | 1;
+                 (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)pGuestTss->ss0, (RTGCPTR)pGuestTss->esp0));
+            pVM->selm.s.Tss.esp1 = pGuestTss->esp0;
+            pVM->selm.s.Tss.ss1  = pGuestTss->ss0 | 1;
             STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
         }
-        if (CPUMGetGuestCR4(pVM) & X86_CR4_VME)
-        {
-            uint32_t offIntRedirBitmap = pGuestTSS->offIoBitmap - sizeof(pVM->selm.s.Tss.IntRedirBitmap);
-
-            /** @todo not sure how the partial case is handled; probably not allowed */
-            if (   offIntRedirBitmap <= offRange
-                && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) >= offRange + cb
-                && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) <= pVM->selm.s.cbGuestTss)
+        /* Handle misaligned TSS in a safe manner (just in case). */
+        else if (   offRange >= RT_UOFFSETOF(VBOXTSS, esp0)
+                 && offRange < RT_UOFFSETOF(VBOXTSS, padding_ss0))
+        {
+            struct
             {
-                Log(("offIoBitmap=%x offIntRedirBitmap=%x cbTSS=%x\n", pGuestTSS->offIoBitmap, offIntRedirBitmap, pVM->selm.s.cbGuestTss));
-                /** @todo only update the changed part. */
-                for (uint32_t i = 0; i < sizeof(pVM->selm.s.Tss.IntRedirBitmap) / 8;i++)
+                uint32_t esp0;
+                uint16_t ss0;
+                uint16_t padding_ss0;
+            } s;
+            AssertCompileSize(s, 8);
+            rc = selmRCReadTssBits(pVM, &s, &pGuestTss->esp0, sizeof(s));
+            if (    rc == VINF_SUCCESS
+                &&  (    s.esp0 !=  pVM->selm.s.Tss.esp1
+                     ||  s.ss0  != (pVM->selm.s.Tss.ss1 & ~1)) /* undo raw-r0 */
+               )
+            {
+                Log(("selmRCGuestTSSWriteHandler: R0 stack: %RTsel:%RGv -> %RTsel:%RGv\n",
+                     (RTSEL)(pVM->selm.s.Tss.ss1 & ~1), (RTGCPTR)pVM->selm.s.Tss.esp1, (RTSEL)s.ss0, (RTGCPTR)s.esp0));
+                pVM->selm.s.Tss.esp1 = s.esp0;
+                pVM->selm.s.Tss.ss1  = s.ss0 | 1;
+                STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandledChanged);
+            }
+        }
+
+        /*
+         * If VME is enabled we need to check if the interrupt redirection bitmap
+         * needs updating.
+         */
+        if (    offRange >= RT_UOFFSETOF(VBOXTSS, offIoBitmap)
+            &&  (CPUMGetGuestCR4(pVM) & X86_CR4_VME))
+        {
+            if (offRange - RT_UOFFSETOF(VBOXTSS, offIoBitmap) < sizeof(pGuestTss->offIoBitmap))
+            {
+                uint16_t offIoBitmap = pGuestTss->offIoBitmap;
+                if (offIoBitmap != pVM->selm.s.offGuestIoBitmap)
                 {
-                    rc = MMGCRamRead(pVM, &pVM->selm.s.Tss.IntRedirBitmap[i * 8], (uint8_t *)pGuestTSS + offIntRedirBitmap + i * 8, 8);
-                    if (RT_FAILURE(rc))
+                    Log(("TSS offIoBitmap changed: old=%#x new=%#x -> resync in ring-3\n", pVM->selm.s.offGuestIoBitmap, offIoBitmap));
+                    VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
+                    VM_FF_SET(pVM, VM_FF_TO_R3);
+                }
+                else
+                    Log(("TSS offIoBitmap: old=%#x new=%#x [unchanged]\n", pVM->selm.s.offGuestIoBitmap, offIoBitmap));
+            }
+            else
+            {
+                /** @todo not sure how the partial case is handled; probably not allowed */
+                uint32_t offIntRedirBitmap = pVM->selm.s.offGuestIoBitmap - sizeof(pVM->selm.s.Tss.IntRedirBitmap);
+                if (   offIntRedirBitmap <= offRange
+                    && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) >= offRange + cb
+                    && offIntRedirBitmap + sizeof(pVM->selm.s.Tss.IntRedirBitmap) <= pVM->selm.s.cbGuestTss)
+                {
+                    Log(("TSS IntRedirBitmap Changed: offIoBitmap=%x offIntRedirBitmap=%x cbTSS=%x offRange=%x cb=%x\n",
+                         pVM->selm.s.offGuestIoBitmap, offIntRedirBitmap, pVM->selm.s.cbGuestTss, offRange, cb));
+
+                    /** @todo only update the changed part. */
+                    for (uint32_t i = 0; i < sizeof(pVM->selm.s.Tss.IntRedirBitmap) / 8; i++)
                     {
-                        /* Shadow page table might be out of sync */
-                        rc = PGMPrefetchPage(pVM, (RTGCPTR)(RTRCUINTPTR)((uint8_t *)pGuestTSS + offIntRedirBitmap + i*8));
-                        if (RT_FAILURE(rc))
-                        {
-                            AssertMsg(rc == VINF_SUCCESS, ("PGMPrefetchPage %RGv failed with %Rrc\n", (RTGCPTR)((uintptr_t)pGuestTSS + offIntRedirBitmap + i*8), rc));
+                        rc = selmRCReadTssBits(pVM, &pVM->selm.s.Tss.IntRedirBitmap[i * 8],
+                                               (uint8_t *)pGuestTss + offIntRedirBitmap + i * 8, 8);
+                        if (rc != VINF_SUCCESS)
                             break;
-                        }
-                        rc = MMGCRamRead(pVM, &pVM->selm.s.Tss.IntRedirBitmap[i * 8], (uint8_t *)pGuestTSS + offIntRedirBitmap + i * 8, 8);
                     }
-                    AssertMsg(rc == VINF_SUCCESS, ("MMGCRamRead %RGv failed with %Rrc\n", (RTGCPTR)((uintptr_t)pGuestTSS + offIntRedirBitmap + i * 8), rc));
+                    STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSRedir);
                 }
-                STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSRedir);
             }
         }
+
+        /* Return to ring-3 for a full resync if any of the above fails... (?) */
+        if (rc != VINF_SUCCESS)
+        {
+            VM_FF_SET(pVM, VM_FF_SELM_SYNC_TSS);
+            VM_FF_SET(pVM, VM_FF_TO_R3);
+            if (RT_SUCCESS(rc))
+                rc = VINF_SUCCESS;
+        }
+
         STAM_COUNTER_INC(&pVM->selm.s.StatRCWriteGuestTSSHandled);
     }
