vmbase: Move dbm to aarch64

Moving management of Dirty pages to aarch64 director as a preparation for adding new CPU Architecture.

Bug: 362733888
Test: m libvmbase

Change-Id: Id8660e8d42229970785d4374b6f55819489961ec
diff --git a/libs/libvmbase/src/arch/aarch64.rs b/libs/libvmbase/src/arch/aarch64.rs
index 2933386..a3f1dd5 100644
--- a/libs/libvmbase/src/arch/aarch64.rs
+++ b/libs/libvmbase/src/arch/aarch64.rs
@@ -14,6 +14,7 @@
 
 //! Wrappers of assembly calls.
 
+pub mod dbm;
 pub mod layout;
 pub mod linker;
 pub mod page_table;
diff --git a/libs/libvmbase/src/arch/aarch64/dbm.rs b/libs/libvmbase/src/arch/aarch64/dbm.rs
new file mode 100644
index 0000000..1c2190c
--- /dev/null
+++ b/libs/libvmbase/src/arch/aarch64/dbm.rs
@@ -0,0 +1,90 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Hardware management of the access flag and dirty state.
+
+use crate::arch::aarch64::page_table::PageTable;
+use crate::arch::flush_region;
+use crate::{dsb, isb, read_sysreg, tlbi, write_sysreg};
+use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion};
+
+/// Sets whether the hardware management of access and dirty state is enabled with
+/// the given boolean.
+pub fn set_dbm_enabled(enabled: bool) {
+    if !dbm_available() {
+        return;
+    }
+    // TCR_EL1.{HA,HD} bits controlling hardware management of access and dirty state
+    const TCR_EL1_HA_HD_BITS: usize = 3 << 39;
+
+    let mut tcr = read_sysreg!("tcr_el1");
+    if enabled {
+        tcr |= TCR_EL1_HA_HD_BITS
+    } else {
+        tcr &= !TCR_EL1_HA_HD_BITS
+    };
+    // SAFETY: Changing this bit in TCR doesn't affect Rust's view of memory.
+    unsafe { write_sysreg!("tcr_el1", tcr) }
+    isb!();
+}
+
+/// Returns `true` if hardware dirty state management is available.
+fn dbm_available() -> bool {
+    if !cfg!(feature = "cpu_feat_hafdbs") {
+        return false;
+    }
+    // Hardware dirty bit management available flag (ID_AA64MMFR1_EL1.HAFDBS[1])
+    const DBM_AVAILABLE: usize = 1 << 1;
+    read_sysreg!("id_aa64mmfr1_el1") & DBM_AVAILABLE != 0
+}
+
+#[allow(clippy::result_unit_err)]
+/// Flushes a memory range the descriptor refers to, if the descriptor is in writable-dirty state.
+pub fn flush_dirty_range(
+    va_range: &MemoryRegion,
+    desc: &Descriptor,
+    _level: usize,
+) -> Result<(), ()> {
+    let flags = desc.flags().ok_or(())?;
+    if !flags.contains(Attributes::READ_ONLY) {
+        flush_region(va_range.start().0, va_range.len());
+    }
+    Ok(())
+}
+
+#[allow(clippy::result_unit_err)]
+/// Clears read-only flag on a PTE, making it writable-dirty. Used when dirty state is managed
+/// in software to handle permission faults on read-only descriptors.
+pub fn mark_dirty_block(
+    va_range: &MemoryRegion,
+    desc: &mut Descriptor,
+    _level: usize,
+) -> Result<(), ()> {
+    let flags = desc.flags().ok_or(())?;
+    if flags.contains(Attributes::DBM) {
+        assert!(flags.contains(Attributes::READ_ONLY), "unexpected PTE writable state");
+        desc.modify_flags(Attributes::empty(), Attributes::READ_ONLY);
+        // Updating the read-only bit of a PTE requires TLB invalidation.
+        // A TLB maintenance instruction is only guaranteed to be complete after a DSB instruction.
+        // An ISB instruction is required to ensure the effects of completed TLB maintenance
+        // instructions are visible to instructions fetched afterwards.
+        // See ARM ARM E2.3.10, and G5.9.
+        tlbi!("vale1", PageTable::ASID, va_range.start().0);
+        dsb!("ish");
+        isb!();
+        Ok(())
+    } else {
+        Err(())
+    }
+}