1 // Copyright 2023, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 //! Hardware management of the access flag and dirty state.
16
17 use super::page_table::PageTable;
18 use super::util::flush_region;
19 use crate::{dsb, isb, read_sysreg, tlbi, write_sysreg};
20 use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion};
21
22 /// Sets whether the hardware management of access and dirty state is enabled with
23 /// the given boolean.
set_dbm_enabled(enabled: bool)24 pub(super) fn set_dbm_enabled(enabled: bool) {
25 if !dbm_available() {
26 return;
27 }
28 // TCR_EL1.{HA,HD} bits controlling hardware management of access and dirty state
29 const TCR_EL1_HA_HD_BITS: usize = 3 << 39;
30
31 let mut tcr = read_sysreg!("tcr_el1");
32 if enabled {
33 tcr |= TCR_EL1_HA_HD_BITS
34 } else {
35 tcr &= !TCR_EL1_HA_HD_BITS
36 };
37 // SAFETY: Changing this bit in TCR doesn't affect Rust's view of memory.
38 unsafe { write_sysreg!("tcr_el1", tcr) }
39 isb!();
40 }
41
42 /// Returns `true` if hardware dirty state management is available.
dbm_available() -> bool43 fn dbm_available() -> bool {
44 if !cfg!(feature = "cpu_feat_hafdbs") {
45 return false;
46 }
47 // Hardware dirty bit management available flag (ID_AA64MMFR1_EL1.HAFDBS[1])
48 const DBM_AVAILABLE: usize = 1 << 1;
49 read_sysreg!("id_aa64mmfr1_el1") & DBM_AVAILABLE != 0
50 }
51
52 /// Flushes a memory range the descriptor refers to, if the descriptor is in writable-dirty state.
flush_dirty_range( va_range: &MemoryRegion, desc: &Descriptor, _level: usize, ) -> Result<(), ()>53 pub(super) fn flush_dirty_range(
54 va_range: &MemoryRegion,
55 desc: &Descriptor,
56 _level: usize,
57 ) -> Result<(), ()> {
58 let flags = desc.flags().ok_or(())?;
59 if !flags.contains(Attributes::READ_ONLY) {
60 flush_region(va_range.start().0, va_range.len());
61 }
62 Ok(())
63 }
64
65 /// Clears read-only flag on a PTE, making it writable-dirty. Used when dirty state is managed
66 /// in software to handle permission faults on read-only descriptors.
mark_dirty_block( va_range: &MemoryRegion, desc: &mut Descriptor, _level: usize, ) -> Result<(), ()>67 pub(super) fn mark_dirty_block(
68 va_range: &MemoryRegion,
69 desc: &mut Descriptor,
70 _level: usize,
71 ) -> Result<(), ()> {
72 let flags = desc.flags().ok_or(())?;
73 if flags.contains(Attributes::DBM) {
74 assert!(flags.contains(Attributes::READ_ONLY), "unexpected PTE writable state");
75 desc.modify_flags(Attributes::empty(), Attributes::READ_ONLY);
76 // Updating the read-only bit of a PTE requires TLB invalidation.
77 // A TLB maintenance instruction is only guaranteed to be complete after a DSB instruction.
78 // An ISB instruction is required to ensure the effects of completed TLB maintenance
79 // instructions are visible to instructions fetched afterwards.
80 // See ARM ARM E2.3.10, and G5.9.
81 tlbi!("vale1", PageTable::ASID, va_range.start().0);
82 dsb!("ish");
83 isb!();
84 Ok(())
85 } else {
86 Err(())
87 }
88 }
89