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}