cortex_m/
cmse.rs

1//! Cortex-M Security Extensions
2//!
3//! This module provides several helper functions to support Armv8-M and Armv8.1-M Security
4//! Extensions.
5//! Most of this implementation is directly inspired by the "Armv8-M Security Extensions:
6//! Requirements on Development Tools" document available here:
7//! https://developer.arm.com/docs/ecm0359818/latest
8//!
9//! Please note that the TT instructions support as described part 4 of the document linked above is
10//! not part of CMSE but is still present in this module. The TT instructions return the
11//! configuration of the Memory Protection Unit at an address.
12//!
13//! # Notes
14//!
15//! * Non-Secure Unprivileged code will always read zeroes from TestTarget and should not use it.
16//! * Non-Secure Privileged code can check current (AccessType::Current) and Non-Secure Unprivileged
17//!   accesses (AccessType::Unprivileged).
18//! * Secure Unprivileged code can check Non-Secure Unprivileged accesses (AccessType::NonSecure).
19//! * Secure Privileged code can check all access types.
20//!
21//! # Example
22//!
23//! ```
24//! use cortex_m::cmse::{TestTarget, AccessType};
25//!
26//! // suspect_address was given by Non-Secure to a Secure function to write at it.
27//! // But is it allowed to?
28//! let suspect_address_test = TestTarget::check(0xDEADBEEF as *mut u32,
29//!                                              AccessType::NonSecureUnprivileged);
30//! if suspect_address_test.ns_read_and_writable() {
31//!     // Non-Secure can not read or write this address!
32//! }
33//! ```
34
35use crate::asm::{tt, tta, ttat, ttt};
36use bitfield::bitfield;
37
38/// Memory access behaviour: determine which privilege execution mode is used and which Memory
39/// Protection Unit (MPU) is used.
40#[derive(PartialEq, Copy, Clone, Debug)]
41pub enum AccessType {
42    /// Access using current privilege level and reading from current security state MPU.
43    /// Uses the TT instruction.
44    Current,
45    /// Unprivileged access reading from current security state MPU. Uses the TTT instruction.
46    Unprivileged,
47    /// Access using current privilege level reading from Non-Secure MPU. Uses the TTA instruction.
48    /// Undefined if used from Non-Secure state.
49    NonSecure,
50    /// Unprivilege access reading from Non-Secure MPU. Uses the TTAT instruction.
51    /// Undefined if used from Non-Secure state.
52    NonSecureUnprivileged,
53}
54
55/// Abstraction of TT instructions and helper functions to determine the security and privilege
56/// attribute of a target address, accessed in different ways.
57#[derive(PartialEq, Copy, Clone, Debug)]
58pub struct TestTarget {
59    tt_resp: TtResp,
60    access_type: AccessType,
61}
62
63bitfield! {
64    /// Test Target Response Payload
65    ///
66    /// Provides the response payload from a TT, TTA, TTT or TTAT instruction.
67    #[derive(PartialEq, Copy, Clone)]
68    struct TtResp(u32);
69    impl Debug;
70    mregion, _: 7, 0;
71    sregion, _: 15, 8;
72    mrvalid, _: 16;
73    srvalid, _: 17;
74    r, _: 18;
75    rw, _: 19;
76    nsr, _: 20;
77    nsrw, _: 21;
78    s, _: 22;
79    irvalid, _: 23;
80    iregion, _: 31, 24;
81}
82
83impl TestTarget {
84    /// Creates a Test Target Response Payload by testing addr using access_type.
85    #[inline]
86    pub fn check(addr: *mut u32, access_type: AccessType) -> Self {
87        let tt_resp = match access_type {
88            AccessType::Current => TtResp(tt(addr)),
89            AccessType::Unprivileged => TtResp(ttt(addr)),
90            AccessType::NonSecure => TtResp(tta(addr)),
91            AccessType::NonSecureUnprivileged => TtResp(ttat(addr)),
92        };
93
94        TestTarget {
95            tt_resp,
96            access_type,
97        }
98    }
99
100    /// Creates a Test Target Response Payload by testing the zone from addr to addr + size - 1
101    /// using access_type.
102    /// Returns None if:
103    ///   * the address zone overlaps SAU, IDAU or MPU region boundaries
104    ///   * size is 0
105    ///   * addr + size - 1 overflows
106    #[inline]
107    pub fn check_range(addr: *mut u32, size: usize, access_type: AccessType) -> Option<Self> {
108        let begin: usize = addr as usize;
109        // Last address of the range (addr + size - 1). This also checks if size is 0.
110        let end: usize = begin.checked_add(size.checked_sub(1)?)?;
111
112        // Regions are aligned at 32-byte boundaries. If the address range fits in one 32-byte
113        // address line, a single TT instruction suffices. This is the case when the following
114        // constraint holds.
115        let single_check: bool = (begin % 32).checked_add(size)? <= 32usize;
116
117        let test_start = TestTarget::check(addr, access_type);
118
119        if single_check {
120            Some(test_start)
121        } else {
122            let test_end = TestTarget::check(end as *mut u32, access_type);
123            // Check that the range does not cross SAU, IDAU or MPU region boundaries.
124            if test_start != test_end {
125                None
126            } else {
127                Some(test_start)
128            }
129        }
130    }
131
132    /// Access type that was used for this test target.
133    #[inline]
134    pub fn access_type(self) -> AccessType {
135        self.access_type
136    }
137
138    /// Get the raw u32 value returned by the TT instruction used.
139    #[inline]
140    pub fn as_u32(self) -> u32 {
141        self.tt_resp.0
142    }
143
144    /// Read accessibility of the target address. Only returns the MPU settings without checking
145    /// the Security state of the target.
146    /// For Unprivileged and NonSecureUnprivileged access types, returns the permissions for
147    /// unprivileged access, regardless of whether the current mode is privileged or unprivileged.
148    /// Returns false if the TT instruction was executed from an unprivileged mode
149    /// and the NonSecure access type was not specified.
150    /// Returns false if the address matches multiple MPU regions.
151    #[inline]
152    pub fn readable(self) -> bool {
153        self.tt_resp.r()
154    }
155
156    /// Read and write accessibility of the target address. Only returns the MPU settings without
157    /// checking the Security state of the target.
158    /// For Unprivileged and NonSecureUnprivileged access types, returns the permissions for
159    /// unprivileged access, regardless of whether the current mode is privileged or unprivileged.
160    /// Returns false if the TT instruction was executed from an unprivileged mode
161    /// and the NonSecure access type was not specified.
162    /// Returns false if the address matches multiple MPU regions.
163    #[inline]
164    pub fn read_and_writable(self) -> bool {
165        self.tt_resp.rw()
166    }
167
168    /// Indicate the MPU region number containing the target address.
169    /// Returns None if the value is not valid:
170    ///   * the MPU is not implemented or MPU_CTRL.ENABLE is set to zero
171    ///   * the register argument specified by the MREGION field does not match any enabled MPU regions
172    ///   * the address matched multiple MPU regions
173    ///   * the address specified by the SREGION field is exempt from the secure memory attribution
174    ///   * the TT instruction was executed from an unprivileged mode and the A flag was not specified.
175    #[inline]
176    pub fn mpu_region(self) -> Option<u8> {
177        if self.tt_resp.srvalid() {
178            // Cast is safe as SREGION field is defined on 8 bits.
179            Some(self.tt_resp.sregion() as u8)
180        } else {
181            None
182        }
183    }
184
185    /// Indicates the Security attribute of the target address. Independent of AccessType.
186    /// Always zero when the test target is done in the Non-Secure state.
187    #[inline]
188    pub fn secure(self) -> bool {
189        self.tt_resp.s()
190    }
191
192    /// Non-Secure Read accessibility of the target address.
193    /// Same as readable() && !secure()
194    #[inline]
195    pub fn ns_readable(self) -> bool {
196        self.tt_resp.nsr()
197    }
198
199    /// Non-Secure Read and Write accessibility of the target address.
200    /// Same as read_and_writable() && !secure()
201    #[inline]
202    pub fn ns_read_and_writable(self) -> bool {
203        self.tt_resp.nsrw()
204    }
205
206    /// Indicate the IDAU region number containing the target address. Independent of AccessType.
207    /// Returns None if the value is not valid:
208    ///   * the IDAU cannot provide a region number
209    ///   * the address is exempt from security attribution
210    ///   * the test target is done from Non-Secure state
211    #[inline]
212    pub fn idau_region(self) -> Option<u8> {
213        if self.tt_resp.irvalid() {
214            // Cast is safe as IREGION field is defined on 8 bits.
215            Some(self.tt_resp.iregion() as u8)
216        } else {
217            None
218        }
219    }
220
221    /// Indicate the SAU region number containing the target address. Independent of AccessType.
222    /// Returns None if the value is not valid:
223    ///   * SAU_CTRL.ENABLE is set to zero
224    ///   * the register argument specified in the SREGION field does not match any enabled SAU regions
225    ///   * the address specified matches multiple enabled SAU regions
226    ///   * the address specified by the SREGION field is exempt from the secure memory attribution
227    ///   * the TT instruction was executed from the Non-secure state or the Security Extension is not
228    ///     implemented
229    #[inline]
230    pub fn sau_region(self) -> Option<u8> {
231        if self.tt_resp.srvalid() {
232            // Cast is safe as SREGION field is defined on 8 bits.
233            Some(self.tt_resp.sregion() as u8)
234        } else {
235            None
236        }
237    }
238}