pvmfw: Implement software dirty state handling

Flush only dirty pages when dropping RW memory regions. Implement
handling of dirty bit in software. Mark writable regions read-only
and make the pages writable-dirty when access causes a permission
fault.

Bug: 269738062
Test: atest MicrodroidTestApp

Change-Id: I2e73a7cc867bae8b68c2a3b68d382405327f99e8
diff --git a/pvmfw/src/helpers.rs b/pvmfw/src/helpers.rs
index 403c7e4..4b669d7 100644
--- a/pvmfw/src/helpers.rs
+++ b/pvmfw/src/helpers.rs
@@ -186,3 +186,46 @@
         core::ffi::CStr::from_bytes_with_nul(concat!($str, "\0").as_bytes()).unwrap()
     }};
 }
+
+/// Executes a data synchronization barrier.
+#[macro_export]
+macro_rules! dsb {
+    ($option:literal) => {{
+        // Safe because this is just a memory barrier and does not affect Rust.
+        #[allow(unused_unsafe)] // In case the macro is used within an unsafe block.
+        unsafe {
+            core::arch::asm!(concat!("dsb ", $option), options(nomem, nostack, preserves_flags));
+        }
+    }};
+}
+
+/// Executes an instruction synchronization barrier.
+#[macro_export]
+macro_rules! isb {
+    () => {{
+        // Safe because this is just a memory barrier and does not affect Rust.
+        #[allow(unused_unsafe)] // In case the macro is used within an unsafe block.
+        unsafe {
+            core::arch::asm!("isb", options(nomem, nostack, preserves_flags));
+        }
+    }};
+}
+
+/// Invalidates cached leaf PTE entries by virtual address.
+#[macro_export]
+macro_rules! tlbi {
+    ($option:literal, $asid:expr, $addr:expr) => {{
+        let asid: usize = $asid;
+        let addr: usize = $addr;
+        // Safe because it invalidates TLB and doesn't affect Rust. When the address matches a
+        // block entry larger than the page size, all translations for the block are invalidated.
+        #[allow(unused_unsafe)] // In case the macro is used within an unsafe block.
+        unsafe {
+            core::arch::asm!(
+                concat!("tlbi ", $option, ", {x}"),
+                x = in(reg) (asid << 48) | (addr >> 12),
+                options(nomem, nostack, preserves_flags)
+            );
+        }
+    }};
+}