Index: /trunk/src/VBox/VMM/VMMR3/NEMR3Native-darwin-armv8.cpp
===================================================================
--- /trunk/src/VBox/VMM/VMMR3/NEMR3Native-darwin-armv8.cpp	(revision 100704)
+++ /trunk/src/VBox/VMM/VMMR3/NEMR3Native-darwin-armv8.cpp	(revision 100705)
@@ -390,6 +390,15 @@
 }
 
-#if 0 /* unused */
-DECLINLINE(int) nemR3DarwinProtectPage(PVM pVM, RTGCPHYS GCPhys, size_t cb, uint32_t fPageProt)
+
+/**
+ * Changes the protection flags for the given guest physical address range.
+ *
+ * @returns VBox status code.
+ * @param   GCPhys              The guest physical address to start mapping.
+ * @param   cb                  The size of the range, page aligned.
+ * @param   fPageProt           The page protection flags to use for this range, combination of NEM_PAGE_PROT_XXX
+ * @param   pu2State            Where to store the state for the new page, optional.
+ */
+DECLINLINE(int) nemR3DarwinProtect(RTGCPHYS GCPhys, size_t cb, uint32_t fPageProt, uint8_t *pu2State)
 {
     hv_memory_flags_t fHvMemProt = 0;
@@ -401,13 +410,17 @@
         fHvMemProt |= HV_MEMORY_EXEC;
 
-    hv_return_t hrc;
-    if (pVM->nem.s.fCreatedAsid)
-        hrc = hv_vm_protect_space(pVM->nem.s.uVmAsid, GCPhys, cb, fHvMemProt);
-    else
-        hrc = hv_vm_protect(GCPhys, cb, fHvMemProt);
-
+    hv_return_t hrc = hv_vm_protect(GCPhys, cb, fHvMemProt);
+    if (hrc == HV_SUCCESS)
+    {
+        if (pu2State)
+            *pu2State = nemR3DarwinPageStateFromProt(fPageProt);
+        return VINF_SUCCESS;
+    }
+
+    LogRel(("nemR3DarwinProtect(%RGp,%zu,%#x): failed! hrc=%#x\n",
+            GCPhys, cb, fPageProt, hrc));
     return nemR3DarwinHvSts2Rc(hrc);
 }
-#endif
+
 
 #ifdef LOG_ENABLED
@@ -892,4 +905,38 @@
 
     RT_NOREF(fL2Fault, GCPtrDataAbrt);
+
+    if (fWrite)
+    {
+        /*
+         * Check whether this is one of the dirty tracked regions, mark it as dirty
+         * and enable write support for this region again.
+         *
+         * This is required for proper VRAM tracking or the display might not get updated
+         * and it is impossible to use the PGM generic facility as it operates on guest page sizes
+         * but setting protection flags with Hypervisor.framework works only host page sized regions, so
+         * we have to cook our own. Additionally the VRAM region is marked as prefetchable (write-back)
+         * which doesn't produce a valid instruction syndrome requiring restarting the instruction after enabling
+         * write access again (due to a missing interpreter right now).
+         */
+        for (uint32_t idSlot = 0; idSlot < RT_ELEMENTS(pVM->nem.s.aMmio2DirtyTracking); idSlot++)
+        {
+            PNEMHVMMIO2REGION pMmio2Region = &pVM->nem.s.aMmio2DirtyTracking[idSlot];
+
+            if (   GCPhysDataAbrt >= pMmio2Region->GCPhysStart
+                && GCPhysDataAbrt <= pMmio2Region->GCPhysLast)
+            {
+                pMmio2Region->fDirty = true;
+
+                uint8_t u2State;
+                int rc = nemR3DarwinProtect(pMmio2Region->GCPhysStart, pMmio2Region->GCPhysLast - pMmio2Region->GCPhysStart + 1,
+                                            NEM_PAGE_PROT_READ | NEM_PAGE_PROT_EXECUTE | NEM_PAGE_PROT_WRITE, &u2State);
+
+                /* Restart the instruction if there is no instruction syndrome available. */
+                if (RT_FAILURE(rc) || !fIsv)
+                    return rc;
+            }
+        }
+    }
+
     AssertReturn(fIsv, VERR_NOT_SUPPORTED); /** @todo Implement using IEM when this should occur. */
 
@@ -1528,5 +1575,5 @@
 {
     RT_NOREF(pVM);
-    return false;
+    return true;
 }
 
@@ -1535,5 +1582,5 @@
                                                   void *pvRam, void *pvMmio2, uint8_t *pu2State, uint32_t *puNemRange)
 {
-    RT_NOREF(pVM, puNemRange, pvRam, fFlags);
+    RT_NOREF(pvRam);
 
     Log5(("NEMR3NotifyPhysMmioExMapEarly: %RGp LB %RGp fFlags=%#x pvRam=%p pvMmio2=%p pu2State=%p (%d)\n",
@@ -1566,5 +1613,37 @@
     {
         Assert(fFlags & NEM_NOTIFY_PHYS_MMIO_EX_F_MMIO2);
-        int rc = nemR3DarwinMap(pVM, GCPhys, pvMmio2, cb, NEM_PAGE_PROT_READ | NEM_PAGE_PROT_WRITE | NEM_PAGE_PROT_EXECUTE, pu2State);
+
+        /* We need to set up our own dirty tracking due to Hypervisor.framework only working on host page sized aligned regions. */
+        uint32_t fProt = NEM_PAGE_PROT_READ | NEM_PAGE_PROT_EXECUTE;
+        if (fFlags & NEM_NOTIFY_PHYS_MMIO_EX_F_TRACK_DIRTY_PAGES)
+        {
+            /* Find a slot for dirty tracking. */
+            PNEMHVMMIO2REGION pMmio2Region = NULL;
+            uint32_t idSlot;
+            for (idSlot = 0; idSlot < RT_ELEMENTS(pVM->nem.s.aMmio2DirtyTracking); idSlot++)
+            {
+                if (   pVM->nem.s.aMmio2DirtyTracking[idSlot].GCPhysStart == 0
+                    && pVM->nem.s.aMmio2DirtyTracking[idSlot].GCPhysLast == 0)
+                {
+                    pMmio2Region = &pVM->nem.s.aMmio2DirtyTracking[idSlot];
+                    break;
+                }
+            }
+
+            if (!pMmio2Region)
+            {
+                LogRel(("NEMR3NotifyPhysMmioExMapEarly: Out of dirty tracking structures -> VERR_NEM_MAP_PAGES_FAILED\n"));
+                return VERR_NEM_MAP_PAGES_FAILED;
+            }
+
+            pMmio2Region->GCPhysStart = GCPhys;
+            pMmio2Region->GCPhysLast  = GCPhys + cb - 1;
+            pMmio2Region->fDirty      = false;
+            *puNemRange = idSlot;
+        }
+        else
+            fProt |= NEM_PAGE_PROT_WRITE;
+
+        int rc = nemR3DarwinMap(pVM, GCPhys, pvMmio2, cb, fProt, pu2State);
         if (RT_FAILURE(rc))
         {
@@ -1617,4 +1696,16 @@
             rc = VERR_NEM_UNMAP_PAGES_FAILED;
         }
+
+        if (fFlags & NEM_NOTIFY_PHYS_MMIO_EX_F_TRACK_DIRTY_PAGES)
+        {
+            /* Reset tracking structure. */
+            uint32_t idSlot = *puNemRange;
+            *puNemRange = UINT32_MAX;
+
+            Assert(idSlot < RT_ELEMENTS(pVM->nem.s.aMmio2DirtyTracking));
+            pVM->nem.s.aMmio2DirtyTracking[idSlot].GCPhysStart = 0;
+            pVM->nem.s.aMmio2DirtyTracking[idSlot].GCPhysLast  = 0;
+            pVM->nem.s.aMmio2DirtyTracking[idSlot].fDirty      = false;
+        }
     }
 
@@ -1652,7 +1743,22 @@
                                                            void *pvBitmap, size_t cbBitmap)
 {
-    RT_NOREF(pVM, GCPhys, cb, uNemRange, pvBitmap, cbBitmap);
-    AssertReleaseFailed();
-    return VERR_NOT_IMPLEMENTED;
+    LogFlowFunc(("NEMR3PhysMmio2QueryAndResetDirtyBitmap: %RGp LB %RGp UnemRange=%u\n", GCPhys, cb, uNemRange));
+    Assert(uNemRange < RT_ELEMENTS(pVM->nem.s.aMmio2DirtyTracking));
+
+    /* Keep it simple for now and mark everything as dirty if it is. */
+    int rc = VINF_SUCCESS;
+    if (pVM->nem.s.aMmio2DirtyTracking[uNemRange].fDirty)
+    {
+        ASMBitSetRange(pvBitmap, 0, cbBitmap * 8);
+
+        pVM->nem.s.aMmio2DirtyTracking[uNemRange].fDirty = false;
+        /* Restore as RX only. */
+        uint8_t u2State;
+        rc = nemR3DarwinProtect(GCPhys, cb, NEM_PAGE_PROT_READ | NEM_PAGE_PROT_EXECUTE, &u2State);
+    }
+    else
+        ASMBitClearRange(pvBitmap, 0, cbBitmap * 8);
+
+    return rc;
 }
 
@@ -1663,5 +1769,5 @@
     RT_NOREF(pVM, GCPhys, cb, pvPages, fFlags, puNemRange);
 
-    Log5(("nemR3NativeNotifyPhysRomRegisterEarly: %RGp LB %RGp pvPages=%p fFlags=%#x\n", GCPhys, cb, pvPages, fFlags));
+    Log5(("NEMR3NotifyPhysRomRegisterEarly: %RGp LB %RGp pvPages=%p fFlags=%#x\n", GCPhys, cb, pvPages, fFlags));
     *pu2State   = UINT8_MAX;
     *puNemRange = 0;
@@ -1673,5 +1779,5 @@
                                                     uint32_t fFlags, uint8_t *pu2State, uint32_t *puNemRange)
 {
-    Log5(("nemR3NativeNotifyPhysRomRegisterLate: %RGp LB %RGp pvPages=%p fFlags=%#x pu2State=%p (%d) puNemRange=%p (%#x)\n",
+    Log5(("NEMR3NotifyPhysRomRegisterLate: %RGp LB %RGp pvPages=%p fFlags=%#x pu2State=%p (%d) puNemRange=%p (%#x)\n",
           GCPhys, cb, pvPages, fFlags, pu2State, *pu2State, puNemRange, *puNemRange));
     *pu2State = UINT8_MAX;
@@ -1682,5 +1788,9 @@
      */
     AssertPtrReturn(pvPages, VERR_INVALID_POINTER);
-    int rc = nemR3DarwinMap(pVM, GCPhys, pvPages, cb, NEM_PAGE_PROT_READ | NEM_PAGE_PROT_EXECUTE, pu2State);
+
+    int rc = nemR3DarwinUnmap(pVM, GCPhys, cb, pu2State);
+    AssertRC(rc);
+
+    rc = nemR3DarwinMap(pVM, GCPhys, pvPages, cb, NEM_PAGE_PROT_READ | NEM_PAGE_PROT_EXECUTE, pu2State);
     if (RT_FAILURE(rc))
     {
@@ -1701,6 +1811,4 @@
                                                         RTR3PTR pvMemR3, uint8_t *pu2State)
 {
-    RT_NOREF(pVM);
-
     Log5(("NEMHCNotifyHandlerPhysicalDeregister: %RGp LB %RGp enmKind=%d pvMemR3=%p pu2State=%p (%d)\n",
           GCPhys, cb, enmKind, pvMemR3, pu2State, *pu2State));
@@ -1710,5 +1818,9 @@
     if (pvMemR3)
     {
-        int rc = nemR3DarwinMap(pVM, GCPhys, pvMemR3, cb, NEM_PAGE_PROT_READ | NEM_PAGE_PROT_WRITE | NEM_PAGE_PROT_EXECUTE, pu2State);
+        /* Unregister what was there before. */
+        int rc = nemR3DarwinUnmap(pVM, GCPhys, cb, pu2State);
+        AssertRC(rc);
+
+        rc = nemR3DarwinMap(pVM, GCPhys, pvMemR3, cb, NEM_PAGE_PROT_READ | NEM_PAGE_PROT_WRITE | NEM_PAGE_PROT_EXECUTE, pu2State);
         AssertLogRelMsgRC(rc, ("NEMHCNotifyHandlerPhysicalDeregister: nemR3DarwinMap(,%p,%RGp,%RGp,) -> %Rrc\n",
                           pvMemR3, GCPhys, cb, rc));
@@ -1750,7 +1862,8 @@
     Log5(("nemHCNativeNotifyPhysPageAllocated: %RGp HCPhys=%RHp fPageProt=%#x enmType=%d *pu2State=%d\n",
           GCPhys, HCPhys, fPageProt, enmType, *pu2State));
-    RT_NOREF(HCPhys, fPageProt, enmType);
-
-    return nemR3DarwinUnmap(pVM, GCPhys, GUEST_PAGE_SIZE, pu2State);
+    RT_NOREF(pVM, GCPhys, HCPhys, fPageProt, enmType, pu2State);
+
+    AssertFailed();
+    return VINF_SUCCESS;
 }
 
@@ -1761,7 +1874,5 @@
     Log5(("NEMHCNotifyPhysPageProtChanged: %RGp HCPhys=%RHp fPageProt=%#x enmType=%d *pu2State=%d\n",
           GCPhys, HCPhys, fPageProt, enmType, *pu2State));
-    RT_NOREF(HCPhys, pvR3, fPageProt, enmType)
-
-    nemR3DarwinUnmap(pVM, GCPhys, GUEST_PAGE_SIZE, pu2State);
+    RT_NOREF(pVM, GCPhys, HCPhys, pvR3, fPageProt, enmType, pu2State);
 }
 
@@ -1772,7 +1883,7 @@
     Log5(("NEMHCNotifyPhysPageChanged: %RGp HCPhys=%RHp->%RHp fPageProt=%#x enmType=%d *pu2State=%d\n",
           GCPhys, HCPhysPrev, HCPhysNew, fPageProt, enmType, *pu2State));
-    RT_NOREF(HCPhysPrev, HCPhysNew, pvNewR3, fPageProt, enmType);
-
-    nemR3DarwinUnmap(pVM, GCPhys, GUEST_PAGE_SIZE, pu2State);
+    RT_NOREF(pVM, GCPhys, HCPhysPrev, HCPhysNew, pvNewR3, fPageProt, enmType, pu2State);
+
+    AssertFailed();
 }
 
Index: /trunk/src/VBox/VMM/include/NEMInternal.h
===================================================================
--- /trunk/src/VBox/VMM/include/NEMInternal.h	(revision 100704)
+++ /trunk/src/VBox/VMM/include/NEMInternal.h	(revision 100705)
@@ -140,4 +140,22 @@
 # define NEM_DARWIN_CPUMCTX_EXTRN_MASK_FOR_IEM_XCPT (IEM_CPUMCTX_EXTRN_XCPT_MASK | NEM_DARWIN_CPUMCTX_EXTRN_MASK_FOR_IEM)
 
+
+# if defined(VBOX_VMM_TARGET_ARMV8)
+/**
+ * MMIO2 tracking region.
+ */
+typedef struct
+{
+    /* Start of the region. */
+    RTGCPHYS                    GCPhysStart;
+    /** End of the region. */
+    RTGCPHYS                    GCPhysLast;
+    /** Whether the region was accessed since last time. */
+    bool                        fDirty;
+} NEMHVMMIO2REGION;
+/** Pointer to a MMIO2 tracking region. */
+typedef NEMHVMMIO2REGION *PNEMHVMMIO2REGION;
+# endif
+
 #endif
 
@@ -293,4 +311,6 @@
     /** The vTimer offset programmed. */
     uint64_t                    u64VTimerOff;
+    /** Dirty tracking slots. */
+    NEMHVMMIO2REGION            aMmio2DirtyTracking[8];
     /** @} */
 # else
