Index: /trunk/src/VBox/VMM/PGMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/PGMInternal.h	(revision 23538)
+++ /trunk/src/VBox/VMM/PGMInternal.h	(revision 23539)
@@ -1042,5 +1042,5 @@
 
 /** Max number of locks on a page. */
-#define PGM_PAGE_MAX_LOCKS                  254
+#define PGM_PAGE_MAX_LOCKS                  UINT8_C(254)
 
 /** Get the read lock count.
@@ -1077,12 +1077,14 @@
 
 
+#if 0
+/** Enables sanity checking of write monitoring using CRC-32.  */
+#define PGMLIVESAVERAMPAGE_WITH_CRC32
+#endif
 
 /**
  * Per page live save tracking data.
  */
-typedef struct PGMLIVESAVEPAGE
-{
-    /** The pass number where this page was last saved.  */
-    uint32_t    uPassSaved;
+typedef struct PGMLIVESAVERAMPAGE
+{
     /** Number of times it has been dirtied. */
     uint32_t    cDirtied : 24;
@@ -1104,10 +1106,18 @@
     /** Bits reserved for future use.  */
     uint32_t    u2Reserved : 2;
-} PGMLIVESAVEPAGE;
-AssertCompileSize(PGMLIVESAVEPAGE, 8);
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+    /** CRC-32 for the page. This is for internal consistency checks.  */
+    uint32_t    u32Crc;
+#endif
+} PGMLIVESAVERAMPAGE;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+AssertCompileSize(PGMLIVESAVERAMPAGE, 8);
+#else
+AssertCompileSize(PGMLIVESAVERAMPAGE, 4);
+#endif
 /** Pointer to the per page live save tracking data. */
-typedef PGMLIVESAVEPAGE *PPGMLIVESAVEPAGE;
-
-/** The max value of PGMLIVESAVEPAGE::cDirtied. */
+typedef PGMLIVESAVERAMPAGE *PPGMLIVESAVERAMPAGE;
+
+/** The max value of PGMLIVESAVERAMPAGE::cDirtied. */
 #define PGMLIVSAVEPAGE_MAX_DIRTIED 0x00fffff0
 
@@ -1140,5 +1150,5 @@
     R3PTRTYPE(void *)                   pvR3;
     /** Live save per page tracking data. */
-    R3PTRTYPE(PPGMLIVESAVEPAGE)         paLSPages;
+    R3PTRTYPE(PPGMLIVESAVERAMPAGE)         paLSPages;
     /** The range description. */
     R3PTRTYPE(const char *)             pszDesc;
@@ -1259,5 +1269,5 @@
  * Live save per page data for an MMIO2 page.
  *
- * Not using PGMLIVESAVEPAGE here because we cannot use normal write monitoring
+ * Not using PGMLIVESAVERAMPAGE here because we cannot use normal write monitoring
  * of MMIO2 pages.  The current approach is using some optimisitic SHA-1 +
  * CRC-32 for detecting changes as well as special handling of zero pages.  This
Index: /trunk/src/VBox/VMM/PGMSavedState.cpp
===================================================================
--- /trunk/src/VBox/VMM/PGMSavedState.cpp	(revision 23538)
+++ /trunk/src/VBox/VMM/PGMSavedState.cpp	(revision 23539)
@@ -92,4 +92,6 @@
 /** @} */
 
+/** The CRC-32 for a zero page. */
+#define PGM_STATE_CRC32_ZERO_PAGE       UINT32_C(0xc71c0011)
 /** The CRC-32 for a zero half page. */
 #define PGM_STATE_CRC32_ZERO_HALF_PAGE  UINT32_C(0xf1e8ba9e)
@@ -1005,5 +1007,5 @@
                 uint32_t const  cPages = pCur->cb >> PAGE_SHIFT;
                 pgmUnlock(pVM);
-                PPGMLIVESAVEPAGE paLSPages = (PPGMLIVESAVEPAGE)MMR3HeapAllocZ(pVM, MM_TAG_PGM, cPages * sizeof(PGMLIVESAVEPAGE));
+                PPGMLIVESAVERAMPAGE paLSPages = (PPGMLIVESAVERAMPAGE)MMR3HeapAllocZ(pVM, MM_TAG_PGM, cPages * sizeof(PGMLIVESAVERAMPAGE));
                 if (!paLSPages)
                     return VERR_NO_MEMORY;
@@ -1026,5 +1028,4 @@
                     /** @todo yield critsect! (after moving this away from EMT0) */
                     PCPGMPAGE pPage = &pCur->aPages[iPage];
-                    paLSPages[iPage].uPassSaved             = UINT32_MAX;
                     paLSPages[iPage].cDirtied               = 0;
                     paLSPages[iPage].fDirty                 = 1; /* everything is dirty at this time */
@@ -1039,4 +1040,7 @@
                                 paLSPages[iPage].fZero   = 1;
                                 paLSPages[iPage].fShared = 0;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                                paLSPages[iPage].u32Crc  = PGM_STATE_CRC32_ZERO_PAGE;
+#endif
                             }
                             else if (PGM_PAGE_IS_SHARED(pPage))
@@ -1044,4 +1048,7 @@
                                 paLSPages[iPage].fZero   = 0;
                                 paLSPages[iPage].fShared = 1;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                                paLSPages[iPage].u32Crc  = UINT32_MAX;
+#endif
                             }
                             else
@@ -1049,4 +1056,7 @@
                                 paLSPages[iPage].fZero   = 0;
                                 paLSPages[iPage].fShared = 0;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                                paLSPages[iPage].u32Crc  = UINT32_MAX;
+#endif
                             }
                             paLSPages[iPage].fIgnore     = 0;
@@ -1061,4 +1071,7 @@
                             paLSPages[iPage].fDirty  = 0;
                             paLSPages[iPage].fIgnore = 1;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                            paLSPages[iPage].u32Crc  = UINT32_MAX;
+#endif
                             pVM->pgm.s.LiveSave.cIgnoredPages++;
                             break;
@@ -1073,4 +1086,7 @@
                             paLSPages[iPage].fDirty  = 0;
                             paLSPages[iPage].fIgnore = 1;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                            paLSPages[iPage].u32Crc  = UINT32_MAX;
+#endif
                             pVM->pgm.s.LiveSave.cIgnoredPages++;
                             break;
@@ -1081,4 +1097,7 @@
                             paLSPages[iPage].fDirty  = 0;
                             paLSPages[iPage].fIgnore = 1;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                            paLSPages[iPage].u32Crc  = UINT32_MAX;
+#endif
                             pVM->pgm.s.LiveSave.cIgnoredPages++;
                             break;
@@ -1093,4 +1112,72 @@
 }
 
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+
+/**
+ * Calculates the CRC-32 for a RAM page and updates the live save page tracking
+ * info with it.
+ *
+ * @param   pVM                 The VM handle.
+ * @param   pCur                The current RAM range.
+ * @param   paLSPages           The current array of live save page tracking
+ *                              structures.
+ * @param   iPage               The page index.
+ */
+static void pgmR3StateCalcCrc32ForRamPage(PVM pVM, PPGMRAMRANGE pCur, PPGMLIVESAVERAMPAGE paLSPages, uint32_t iPage)
+{
+    RTGCPHYS    GCPhys = pCur->GCPhys + ((RTGCPHYS)iPage << PAGE_SHIFT);
+    void const *pvPage;
+    int rc = pgmPhysGCPhys2CCPtrInternalReadOnly(pVM, &pCur->aPages[iPage], GCPhys, &pvPage);
+    if (RT_SUCCESS(rc))
+        paLSPages[iPage].u32Crc = RTCrc32(pvPage, PAGE_SIZE);
+    else
+        paLSPages[iPage].u32Crc = UINT32_MAX; /* Invalid */
+}
+
+
+/**
+ * Verifies the CRC-32 for a page given it's raw bits.
+ *
+ * @param   pvPage              The page bits.
+ * @param   pCur                The current RAM range.
+ * @param   paLSPages           The current array of live save page tracking
+ *                              structures.
+ * @param   iPage               The page index.
+ */
+static void pgmR3StateVerifyCrc32ForPage(void const *pvPage, PPGMRAMRANGE pCur, PPGMLIVESAVERAMPAGE paLSPages, uint32_t iPage)
+{
+    if (paLSPages[iPage].u32Crc != UINT32_MAX)
+    {
+        uint32_t u32Crc = RTCrc32(pvPage, PAGE_SIZE);
+        Assert(!PGM_PAGE_IS_ZERO(&pCur->aPages[iPage]) || u32Crc == PGM_STATE_CRC32_ZERO_PAGE);
+        AssertMsg(paLSPages[iPage].u32Crc == u32Crc,
+                  ("%08x != %08x for %RGp %R[pgmpage]\n", paLSPages[iPage].u32Crc, u32Crc,
+                   pCur->GCPhys + ((RTGCPHYS)iPage << PAGE_SHIFT), &pCur->aPages[iPage]));
+    }
+}
+
+
+/**
+ * Verfies the CRC-32 for a RAM page.
+ *
+ * @param   pVM                 The VM handle.
+ * @param   pCur                The current RAM range.
+ * @param   paLSPages           The current array of live save page tracking
+ *                              structures.
+ * @param   iPage               The page index.
+ */
+static void pgmR3StateVerifyCrc32ForRamPage(PVM pVM, PPGMRAMRANGE pCur, PPGMLIVESAVERAMPAGE paLSPages, uint32_t iPage)
+{
+    if (paLSPages[iPage].u32Crc != UINT32_MAX)
+    {
+        RTGCPHYS    GCPhys = pCur->GCPhys + ((RTGCPHYS)iPage << PAGE_SHIFT);
+        void const *pvPage;
+        int rc = pgmPhysGCPhys2CCPtrInternalReadOnly(pVM, &pCur->aPages[iPage], GCPhys, &pvPage);
+        if (RT_SUCCESS(rc))
+            pgmR3StateVerifyCrc32ForPage(pvPage, pCur, paLSPages, iPage);
+    }
+}
+
+#endif /* PGMLIVESAVERAMPAGE_WITH_CRC32 */
 
 /**
@@ -1116,5 +1203,5 @@
                 && !PGM_RAM_RANGE_IS_AD_HOC(pCur))
             {
-                PPGMLIVESAVEPAGE paLSPages = pCur->paLSPages;
+                PPGMLIVESAVERAMPAGE paLSPages = pCur->paLSPages;
                 uint32_t         cPages    = pCur->cb >> PAGE_SHIFT;
                 uint32_t         iPage     = GCPhysCur <= pCur->GCPhys ? 0 : (GCPhysCur - pCur->GCPhys) >> PAGE_SHIFT;
@@ -1124,5 +1211,7 @@
                     /* Do yield first. */
                     if (   !fFinalPass
+#ifndef PGMLIVESAVERAMPAGE_WITH_CRC32
                         && (iPage & 0x7ff) == 0x100
+#endif
                         && PDMR3CritSectYield(&pVM->pgm.s.CritSect)
                         && pVM->pgm.s.idRamRangesGen != idRamRangesGen)
@@ -1176,4 +1265,7 @@
                                 paLSPages[iPage].fZero                  = 0;
                                 paLSPages[iPage].fShared                = 0;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                                paLSPages[iPage].u32Crc                 = UINT32_MAX; /* invalid */
+#endif
                                 break;
 
@@ -1181,8 +1273,19 @@
                                 Assert(paLSPages[iPage].fWriteMonitored);
                                 if (PGM_PAGE_GET_WRITE_LOCKS(&pCur->aPages[iPage]) == 0)
+                                {
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                                    if (paLSPages[iPage].fWriteMonitoredJustNow)
+                                        pgmR3StateCalcCrc32ForRamPage(pVM, pCur, paLSPages, iPage);
+                                    else
+                                        pgmR3StateVerifyCrc32ForRamPage(pVM, pCur, paLSPages, iPage);
+#endif
                                     paLSPages[iPage].fWriteMonitoredJustNow = 0;
+                                }
                                 else
                                 {
                                     paLSPages[iPage].fWriteMonitoredJustNow = 1;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                                    paLSPages[iPage].u32Crc                 = UINT32_MAX; /* invalid */
+#endif
                                     if (!paLSPages[iPage].fDirty)
                                     {
@@ -1200,4 +1303,7 @@
                                     paLSPages[iPage].fZero = 1;
                                     paLSPages[iPage].fShared = 0;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                                    paLSPages[iPage].u32Crc = PGM_STATE_CRC32_ZERO_PAGE;
+#endif
                                     if (!paLSPages[iPage].fDirty)
                                     {
@@ -1215,4 +1321,7 @@
                                     paLSPages[iPage].fZero = 0;
                                     paLSPages[iPage].fShared = 1;
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                                    pgmR3StateCalcCrc32ForRamPage(pVM, pCur, paLSPages, iPage);
+#endif
                                     if (!paLSPages[iPage].fDirty)
                                     {
@@ -1297,5 +1406,5 @@
                 && !PGM_RAM_RANGE_IS_AD_HOC(pCur))
             {
-                PPGMLIVESAVEPAGE paLSPages = pCur->paLSPages;
+                PPGMLIVESAVERAMPAGE paLSPages = pCur->paLSPages;
                 uint32_t         cPages    = pCur->cb >> PAGE_SHIFT;
                 uint32_t         iPage     = GCPhysCur <= pCur->GCPhys ? 0 : (GCPhysCur - pCur->GCPhys) >> PAGE_SHIFT;
@@ -1342,5 +1451,11 @@
                             && !paLSPages[iPage].fDirty
                             && !paLSPages[iPage].fIgnore)
+                        {
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                            if (PGM_PAGE_GET_TYPE(&pCur->aPages[iPage]) != PGMPAGETYPE_RAM)
+                                pgmR3StateVerifyCrc32ForRamPage(pVM, pCur, paLSPages, iPage);
+#endif
                             continue;
+                        }
                         if (PGM_PAGE_GET_TYPE(&pCur->aPages[iPage]) != PGMPAGETYPE_RAM)
                             continue;
@@ -1360,9 +1475,15 @@
                          * SSM call may block).
                          */
-                        char        abPage[PAGE_SIZE];
+                        uint8_t     abPage[PAGE_SIZE];
                         void const *pvPage;
                         rc = pgmPhysGCPhys2CCPtrInternalReadOnly(pVM, &pCur->aPages[iPage], GCPhys, &pvPage);
                         if (RT_SUCCESS(rc))
+                        {
                             memcpy(abPage, pvPage, PAGE_SIZE);
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                            if (paLSPages)
+                                pgmR3StateVerifyCrc32ForPage(abPage, pCur, paLSPages, iPage);
+#endif
+                        }
                         pgmUnlock(pVM);
                         AssertLogRelMsgRCReturn(rc, ("rc=%Rrc GCPhys=%RGp\n", rc, GCPhys), rc);
@@ -1382,4 +1503,8 @@
                          * Dirty zero page.
                          */
+#ifdef PGMLIVESAVERAMPAGE_WITH_CRC32
+                        if (paLSPages)
+                            pgmR3StateVerifyCrc32ForRamPage(pVM, pCur, paLSPages, iPage);
+#endif
                         pgmUnlock(pVM);
 
@@ -1400,5 +1525,4 @@
                     {
                         paLSPages[iPage].fDirty = 0;
-                        paLSPages[iPage].uPassSaved = uPass;
                         pVM->pgm.s.LiveSave.Ram.cReadyPages++;
                         pVM->pgm.s.LiveSave.Ram.cDirtyPages--;
@@ -1544,5 +1668,5 @@
 }
 
-//#include <iprt/stream.h>
+#include <iprt/stream.h>
 
 /**
@@ -1556,5 +1680,5 @@
 static DECLCALLBACK(int)  pgmR3LiveVote(PVM pVM, PSSMHANDLE pSSM)
 {
-#if 0
+#if 1
     RTPrintf("# Rom[R/D/Z/M]=%03x/%03x/%03x/%03x  Mmio2=%04x/%04x/%04x/%04x  Ram=%06x/%06x/%06x/%06x Ignored=%03x\n",
              pVM->pgm.s.LiveSave.Rom.cReadyPages,
@@ -1575,4 +1699,6 @@
     if ((++s_iHack % 42) == 0)
         return VINF_SUCCESS;
+    RTThreadSleep(1000);
+
 #else
     if (      pVM->pgm.s.LiveSave.Rom.cDirtyPages
