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(())
+ }
+}