blob: 401022e35e363eb8603965fdbd6745f29878bacf [file] [log] [blame]
Alice Wang4dd20932023-05-26 13:47:16 +00001// 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
Pierre-Clément Tosia33f2df2023-06-07 16:20:17 +000017use super::page_table::{is_leaf_pte, PageTable};
Alice Wang3fa9b802023-06-06 07:52:31 +000018use super::util::flush_region;
Alice Wangb73a81b2023-06-07 13:05:09 +000019use crate::{dsb, isb, read_sysreg, tlbi, write_sysreg};
Alice Wang3fa9b802023-06-06 07:52:31 +000020use aarch64_paging::paging::{Attributes, Descriptor, MemoryRegion};
Alice Wang4dd20932023-05-26 13:47:16 +000021
22/// Sets whether the hardware management of access and dirty state is enabled with
23/// the given boolean.
Alice Wang93ee98a2023-06-08 08:20:39 +000024pub(super) fn set_dbm_enabled(enabled: bool) {
Alice Wang4dd20932023-05-26 13:47:16 +000025 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 };
Andrew Walbranc06e7342023-07-05 14:00:51 +000037 // SAFETY: Changing this bit in TCR doesn't affect Rust's view of memory.
Alice Wang4dd20932023-05-26 13:47:16 +000038 unsafe { write_sysreg!("tcr_el1", tcr) }
39 isb!();
40}
41
42/// Returns `true` if hardware dirty state management is available.
43fn 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}
Alice Wang3fa9b802023-06-06 07:52:31 +000051
52/// Flushes a memory range the descriptor refers to, if the descriptor is in writable-dirty state.
Alice Wang93ee98a2023-06-08 08:20:39 +000053pub(super) fn flush_dirty_range(
Alice Wang3fa9b802023-06-06 07:52:31 +000054 va_range: &MemoryRegion,
55 desc: &mut Descriptor,
56 level: usize,
57) -> Result<(), ()> {
58 // Only flush ranges corresponding to dirty leaf PTEs.
59 let flags = desc.flags().ok_or(())?;
60 if !is_leaf_pte(&flags, level) {
61 return Ok(());
62 }
63 if !flags.contains(Attributes::READ_ONLY) {
64 flush_region(va_range.start().0, va_range.len());
65 }
66 Ok(())
67}
Alice Wangb73a81b2023-06-07 13:05:09 +000068
69/// Clears read-only flag on a PTE, making it writable-dirty. Used when dirty state is managed
70/// in software to handle permission faults on read-only descriptors.
Alice Wang93ee98a2023-06-08 08:20:39 +000071pub(super) fn mark_dirty_block(
Alice Wangb73a81b2023-06-07 13:05:09 +000072 va_range: &MemoryRegion,
73 desc: &mut Descriptor,
74 level: usize,
75) -> Result<(), ()> {
76 let flags = desc.flags().ok_or(())?;
77 if !is_leaf_pte(&flags, level) {
78 return Ok(());
79 }
80 if flags.contains(Attributes::DBM) {
81 assert!(flags.contains(Attributes::READ_ONLY), "unexpected PTE writable state");
82 desc.modify_flags(Attributes::empty(), Attributes::READ_ONLY);
83 // Updating the read-only bit of a PTE requires TLB invalidation.
84 // A TLB maintenance instruction is only guaranteed to be complete after a DSB instruction.
85 // An ISB instruction is required to ensure the effects of completed TLB maintenance
86 // instructions are visible to instructions fetched afterwards.
87 // See ARM ARM E2.3.10, and G5.9.
Pierre-Clément Tosia33f2df2023-06-07 16:20:17 +000088 tlbi!("vale1", PageTable::ASID, va_range.start().0);
Alice Wangb73a81b2023-06-07 13:05:09 +000089 dsb!("ish");
90 isb!();
91 Ok(())
92 } else {
93 Err(())
94 }
95}