Index: kernel/arch/ia64/src/mm/tlb.c
===================================================================
--- kernel/arch/ia64/src/mm/tlb.c	(revision 38dc82d20695b43a799be28d4fd2b2cd2c5bb785)
+++ kernel/arch/ia64/src/mm/tlb.c	(revision 346b12a2767bb66df8633e469fd871fea8c43b5c)
@@ -662,4 +662,5 @@
 		t.d = true;
 		dtc_pte_copy(&t);
+		page_mapping_update(as, va, true, &t);
 	} else {
 		as_page_fault(va, PF_ACCESS_WRITE, istate);
@@ -694,4 +695,5 @@
 		t.a = true;
 		itc_pte_copy(&t);
+		page_mapping_update(AS, va, true, &t);
 	} else {
 		as_page_fault(va, PF_ACCESS_EXEC, istate);
@@ -728,4 +730,5 @@
 		t.a = true;
 		dtc_pte_copy(&t);
+		page_mapping_update(as, va, true, &t);
 	} else {
 		if (as_page_fault(va, PF_ACCESS_READ, istate) == AS_PF_FAULT) {
Index: kernel/arch/mips32/src/mm/tlb.c
===================================================================
--- kernel/arch/mips32/src/mm/tlb.c	(revision 38dc82d20695b43a799be28d4fd2b2cd2c5bb785)
+++ kernel/arch/mips32/src/mm/tlb.c	(revision 346b12a2767bb66df8633e469fd871fea8c43b5c)
@@ -111,4 +111,6 @@
 		    pte.cacheable, pte.pfn);
 
+		page_mapping_update(AS, badvaddr, true, &pte);
+
 		/*
 		 * New entry is to be inserted into TLB
@@ -176,4 +178,6 @@
 		tlb_prepare_entry_lo(&lo, pte.g, pte.p, pte.d,
 		    pte.cacheable, pte.pfn);
+
+		page_mapping_update(AS, badvaddr, true, &pte);
 
 		/*
@@ -239,4 +243,6 @@
 		tlb_prepare_entry_lo(&lo, pte.g, pte.p, pte.w,
 		    pte.cacheable, pte.pfn);
+
+		page_mapping_update(AS, badvaddr, true, &pte);
 
 		/*
Index: kernel/arch/sparc64/src/mm/sun4u/tlb.c
===================================================================
--- kernel/arch/sparc64/src/mm/sun4u/tlb.c	(revision 38dc82d20695b43a799be28d4fd2b2cd2c5bb785)
+++ kernel/arch/sparc64/src/mm/sun4u/tlb.c	(revision 346b12a2767bb66df8633e469fd871fea8c43b5c)
@@ -210,4 +210,5 @@
 		itsb_pte_copy(&t, index);
 #endif
+		page_mapping_update(AS, istate->tpc, true, &t);
 	} else {
 		/*
@@ -264,4 +265,5 @@
 		dtsb_pte_copy(&t, index, true);
 #endif
+		page_mapping_update(as, page_16k, true, &t);
 	} else {
 		/*
@@ -308,4 +310,5 @@
 		dtsb_pte_copy(&t, index, false);
 #endif
+		page_mapping_update(as, page_16k, true, &t);
 	} else {
 		/*
Index: kernel/arch/sparc64/src/mm/sun4v/tlb.c
===================================================================
--- kernel/arch/sparc64/src/mm/sun4v/tlb.c	(revision 38dc82d20695b43a799be28d4fd2b2cd2c5bb785)
+++ kernel/arch/sparc64/src/mm/sun4v/tlb.c	(revision 346b12a2767bb66df8633e469fd871fea8c43b5c)
@@ -224,4 +224,5 @@
 		itsb_pte_copy(&t);
 #endif
+		page_mapping_update(AS, va, true, &t);
 	} else {
 		/*
@@ -271,4 +272,5 @@
 		dtsb_pte_copy(&t, true);
 #endif
+		page_mapping_update(as, va, true, &t);
 	} else {
 		/*
@@ -309,4 +311,5 @@
 		dtsb_pte_copy(&t, false);
 #endif
+		page_mapping_update(as, va, true, &t);
 	} else {
 		/*
Index: kernel/genarch/src/mm/page_ht.c
===================================================================
--- kernel/genarch/src/mm/page_ht.c	(revision 38dc82d20695b43a799be28d4fd2b2cd2c5bb785)
+++ kernel/genarch/src/mm/page_ht.c	(revision 346b12a2767bb66df8633e469fd871fea8c43b5c)
@@ -60,4 +60,5 @@
 static void ht_mapping_remove(as_t *, uintptr_t);
 static bool ht_mapping_find(as_t *, uintptr_t, bool, pte_t *);
+static void ht_mapping_update(as_t *, uintptr_t, bool, pte_t *);
 static void ht_mapping_make_global(uintptr_t, size_t);
 
@@ -91,4 +92,5 @@
 	.mapping_remove = ht_mapping_remove,
 	.mapping_find = ht_mapping_find,
+	.mapping_update = ht_mapping_update,
 	.mapping_make_global = ht_mapping_make_global
 };
@@ -245,4 +247,19 @@
 }
 
+static pte_t *ht_mapping_find_internal(as_t *as, uintptr_t page, bool nolock)
+{
+	sysarg_t key[2] = {
+		(uintptr_t) as,
+		page = ALIGN_DOWN(page, PAGE_SIZE)
+	};
+
+	ASSERT(nolock || page_table_locked(as));
+	
+	link_t *cur = hash_table_find(&page_ht, key);
+	if (cur)
+		return hash_table_get_instance(cur, pte_t, link);
+	
+	return NULL;
+}
 
 /** Find mapping for virtual page in page hash table.
@@ -257,16 +274,36 @@
 bool ht_mapping_find(as_t *as, uintptr_t page, bool nolock, pte_t *pte)
 {
-	sysarg_t key[2] = {
-		(uintptr_t) as,
-		page = ALIGN_DOWN(page, PAGE_SIZE)
-	};
-
-	ASSERT(nolock || page_table_locked(as));
-	
-	link_t *cur = hash_table_find(&page_ht, key);
-	if (cur)
-		*pte = *hash_table_get_instance(cur, pte_t, link);
-	
-	return cur != NULL;
+	pte_t *t = ht_mapping_find_internal(as, page, nolock);
+	if (t)
+		*pte = *t;
+	
+	return t != NULL;
+}
+
+/** Update mapping for virtual page in page hash table.
+ *
+ * @param as       Address space to which page belongs.
+ * @param page     Virtual page.
+ * @param nolock   True if the page tables need not be locked.
+ * @param pte      New PTE.
+ */
+void ht_mapping_update(as_t *as, uintptr_t page, bool nolock, pte_t *pte)
+{
+	pte_t *t = ht_mapping_find_internal(as, page, nolock);
+	if (!t)
+		panic("Updating non-existent PTE");
+	
+	ASSERT(pte->as == t->as);
+	ASSERT(pte->page == t->page);
+	ASSERT(pte->frame == t->frame);
+	ASSERT(pte->g == t->g);
+	ASSERT(pte->x == t->x);
+	ASSERT(pte->w == t->w);
+	ASSERT(pte->k == t->k);
+	ASSERT(pte->c == t->c);
+	ASSERT(pte->p == t->p);
+
+	t->a = pte->a;
+	t->d = pte->d;
 }
 
Index: kernel/genarch/src/mm/page_pt.c
===================================================================
--- kernel/genarch/src/mm/page_pt.c	(revision 38dc82d20695b43a799be28d4fd2b2cd2c5bb785)
+++ kernel/genarch/src/mm/page_pt.c	(revision 346b12a2767bb66df8633e469fd871fea8c43b5c)
@@ -54,4 +54,5 @@
 static void pt_mapping_remove(as_t *, uintptr_t);
 static bool pt_mapping_find(as_t *, uintptr_t, bool, pte_t *pte);
+static void pt_mapping_update(as_t *, uintptr_t, bool, pte_t *pte);
 static void pt_mapping_make_global(uintptr_t, size_t);
 
@@ -60,4 +61,5 @@
 	.mapping_remove = pt_mapping_remove,
 	.mapping_find = pt_mapping_find,
+	.mapping_update = pt_mapping_update,
 	.mapping_make_global = pt_mapping_make_global
 };
@@ -289,4 +291,41 @@
 }
 
+static pte_t *pt_mapping_find_internal(as_t *as, uintptr_t page, bool nolock)
+{
+	ASSERT(nolock || page_table_locked(as));
+
+	pte_t *ptl0 = (pte_t *) PA2KA((uintptr_t) as->genarch.page_table);
+	if (GET_PTL1_FLAGS(ptl0, PTL0_INDEX(page)) & PAGE_NOT_PRESENT)
+		return NULL;
+
+	read_barrier();
+	
+	pte_t *ptl1 = (pte_t *) PA2KA(GET_PTL1_ADDRESS(ptl0, PTL0_INDEX(page)));
+	if (GET_PTL2_FLAGS(ptl1, PTL1_INDEX(page)) & PAGE_NOT_PRESENT)
+		return NULL;
+
+#if (PTL1_ENTRIES != 0)
+	/*
+	 * Always read ptl2 only after we are sure it is present.
+	 */
+	read_barrier();
+#endif
+	
+	pte_t *ptl2 = (pte_t *) PA2KA(GET_PTL2_ADDRESS(ptl1, PTL1_INDEX(page)));
+	if (GET_PTL3_FLAGS(ptl2, PTL2_INDEX(page)) & PAGE_NOT_PRESENT)
+		return NULL;
+
+#if (PTL2_ENTRIES != 0)
+	/*
+	 * Always read ptl3 only after we are sure it is present.
+	 */
+	read_barrier();
+#endif
+	
+	pte_t *ptl3 = (pte_t *) PA2KA(GET_PTL3_ADDRESS(ptl2, PTL2_INDEX(page)));
+	
+	return &ptl3[PTL3_INDEX(page)];
+}
+
 /** Find mapping for virtual page in hierarchical page tables.
  *
@@ -300,38 +339,30 @@
 bool pt_mapping_find(as_t *as, uintptr_t page, bool nolock, pte_t *pte)
 {
-	ASSERT(nolock || page_table_locked(as));
-
-	pte_t *ptl0 = (pte_t *) PA2KA((uintptr_t) as->genarch.page_table);
-	if (GET_PTL1_FLAGS(ptl0, PTL0_INDEX(page)) & PAGE_NOT_PRESENT)
-		return false;
-
-	read_barrier();
-	
-	pte_t *ptl1 = (pte_t *) PA2KA(GET_PTL1_ADDRESS(ptl0, PTL0_INDEX(page)));
-	if (GET_PTL2_FLAGS(ptl1, PTL1_INDEX(page)) & PAGE_NOT_PRESENT)
-		return false;
-
-#if (PTL1_ENTRIES != 0)
-	/*
-	 * Always read ptl2 only after we are sure it is present.
-	 */
-	read_barrier();
-#endif
-	
-	pte_t *ptl2 = (pte_t *) PA2KA(GET_PTL2_ADDRESS(ptl1, PTL1_INDEX(page)));
-	if (GET_PTL3_FLAGS(ptl2, PTL2_INDEX(page)) & PAGE_NOT_PRESENT)
-		return false;
-
-#if (PTL2_ENTRIES != 0)
-	/*
-	 * Always read ptl3 only after we are sure it is present.
-	 */
-	read_barrier();
-#endif
-	
-	pte_t *ptl3 = (pte_t *) PA2KA(GET_PTL3_ADDRESS(ptl2, PTL2_INDEX(page)));
-	
-	*pte = ptl3[PTL3_INDEX(page)];
-	return true;
+	pte_t *t = pt_mapping_find_internal(as, page, nolock);
+	if (t)
+		*pte = *t;
+	return t != NULL;
+}
+
+/** Update mapping for virtual page in hierarchical page tables.
+ *
+ * @param as       Address space to which page belongs.
+ * @param page     Virtual page.
+ * @param nolock   True if the page tables need not be locked.
+ * @param[in] pte  New PTE.
+ */
+void pt_mapping_update(as_t *as, uintptr_t page, bool nolock, pte_t *pte)
+{
+	pte_t *t = pt_mapping_find_internal(as, page, nolock);
+	if (!t)
+		panic("Updating non-existent PTE");	
+
+	ASSERT(PTE_VALID(t) == PTE_VALID(pte));
+	ASSERT(PTE_PRESENT(t) == PTE_PRESENT(pte));
+	ASSERT(PTE_GET_FRAME(t) == PTE_GET_FRAME(pte));
+	ASSERT(PTE_WRITABLE(t) == PTE_WRITABLE(pte));
+	ASSERT(PTE_EXECUTABLE(t) == PTE_EXECUTABLE(pte));
+
+	*t = *pte;
 }
 
Index: kernel/generic/include/mm/page.h
===================================================================
--- kernel/generic/include/mm/page.h	(revision 38dc82d20695b43a799be28d4fd2b2cd2c5bb785)
+++ kernel/generic/include/mm/page.h	(revision 346b12a2767bb66df8633e469fd871fea8c43b5c)
@@ -49,4 +49,5 @@
 	void (* mapping_remove)(as_t *, uintptr_t);
 	bool (* mapping_find)(as_t *, uintptr_t, bool, pte_t *);
+	void (* mapping_update)(as_t *, uintptr_t, bool, pte_t *);
 	void (* mapping_make_global)(uintptr_t, size_t);
 } page_mapping_operations_t;
@@ -61,4 +62,5 @@
 extern void page_mapping_remove(as_t *, uintptr_t);
 extern bool page_mapping_find(as_t *, uintptr_t, bool, pte_t *);
+extern void page_mapping_update(as_t *, uintptr_t, bool, pte_t *);
 extern void page_mapping_make_global(uintptr_t, size_t);
 extern pte_t *page_table_create(unsigned int);
Index: kernel/generic/src/mm/page.c
===================================================================
--- kernel/generic/src/mm/page.c	(revision 38dc82d20695b43a799be28d4fd2b2cd2c5bb785)
+++ kernel/generic/src/mm/page.c	(revision 346b12a2767bb66df8633e469fd871fea8c43b5c)
@@ -156,4 +156,25 @@
 }
 
+/** Update mapping for virtual page.
+ *
+ * Use only to update accessed and modified/dirty bits.
+ *
+ * @param as       Address space to which page belongs.
+ * @param page     Virtual page.
+ * @param nolock   True if the page tables need not be locked.
+ * @param pte      New PTE.
+ */
+NO_TRACE void page_mapping_update(as_t *as, uintptr_t page, bool nolock,
+    pte_t *pte)
+{
+	ASSERT(nolock || page_table_locked(as));
+	
+	ASSERT(page_mapping_operations);
+	ASSERT(page_mapping_operations->mapping_find);
+	
+	page_mapping_operations->mapping_update(as,
+	    ALIGN_DOWN(page, PAGE_SIZE), nolock, pte);
+}
+
 /** Make the mapping shared by all page tables (not address spaces).
  * 
