1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
//! Implementation of the ARM memory protection unit. use kernel; use kernel::common::VolatileCell; use kernel::common::math::PowerOfTwo; /// Indicates whether the MPU is present and, if so, how many regions it /// supports. #[repr(C)] pub struct MpuType { /// Indicates whether the processor support unified (0) or separate /// (1) instruction and data regions. Always reads 0 on the /// Cortex-M4. pub is_separate: VolatileCell<u8>, /// The number of data regions supported. If this field reads-as-zero the /// processor does not implement an MPU pub data_regions: VolatileCell<u8>, /// The number of instructions regions supported. Always reads 0. pub instruction_regions: VolatileCell<u8>, _reserved: u8, } #[repr(C)] /// MPU Registers for the Cortex-M4 family /// /// Described in section 4.5 of /// <http://infocenter.arm.com/help/topic/com.arm.doc.dui0553a/DUI0553A_cortex_m4_dgug.pdf> pub struct Registers { pub mpu_type: VolatileCell<MpuType>, /// The control register: /// /// * Enables the MPU (bit 0). /// * Enables MPU in hard-fault, non-maskable interrupt (NMI) and /// FAULTMASK escalated handlers (bit 1). /// * Enables the default memory map background region in privileged mode /// (bit 2). /// /// ```text /// Bit | Name | Function /// ----- | ---------- | ----------------------------- /// 0 | ENABLE | Enable the MPU (1=enabled) /// 1 | HFNMIENA | 0=MPU disabled during HardFault, NMI, and FAULTMASK /// | | regardless of bit 0. 1 leaves enabled. /// 2 | PRIVDEFENA | 0=Any memory access not explicitly enabled causes fault /// | | 1=Privledged mode code can read any memory address /// ``` pub control: VolatileCell<u32>, /// Selects the region number (zero-indexed) referenced by the region base /// address and region attribute and size registers. /// /// ```text /// Bit | Name | Function /// ----- | -------- | ----------------------------- /// [7:0] | REGION | Region for writes to MPU_RBAR or MPU_RASR. Range 0-7. /// ``` pub region_number: VolatileCell<u32>, /// Defines the base address of the currently selected MPU region. /// /// When writing, the first 3 bits select a new region if bit-4 is set. /// /// The top bits set the base address of the register, with the bottom 32-N /// bits masked based on the region size (set in the region attribute and /// size register) according to: /// /// N = Log2(Region size in bytes) /// /// ```text /// Bit | Name | Function /// --------- | ------- | ----------------------------- /// [31:N] | ADDR | Region base address /// [(N-1):5] | | Reserved /// [4] | VALID | {RZ} 0=Use region_number reg, 1=Use REGION /// | | Update base address for chosen region /// [3:0] | REGION | {W} (see VALID) ; {R} return region_number reg /// ``` pub region_base_address: VolatileCell<u32>, /// Defines the region size and memory attributes of the selected MPU /// region. The bits are defined as in 4.5.5 of the Cortex-M4 user guide: /// /// ```text /// Bit | Name | Function /// ----- | ------ | ----------------------------- /// 0 | ENABLE | Region enable /// 5:1 | SIZE | Region size is 2^(SIZE+1) (minimum 3) /// 7:6 | | Unused /// 15:8 | SRD | Subregion disable bits (0 is enable, 1 is disable) /// 16 | B | Memory access attribute /// 17 | C | Memory access attribute /// 18 | S | Shareable /// 21:19 | TEX | Memory access attribute /// 23:22 | | Unused /// 26:24 | AP | Access permission field /// 27 | | Unused /// 28 | XN | Instruction access disable /// ``` pub region_attributes_and_size: VolatileCell<u32>, } const MPU_BASE_ADDRESS: *const Registers = 0xE000ED90 as *const Registers; /// Constructor field is private to limit who can create a new MPU pub struct MPU(*const Registers); impl MPU { pub const unsafe fn new() -> MPU { MPU(MPU_BASE_ADDRESS) } } type Region = kernel::mpu::Region; impl kernel::mpu::MPU for MPU { fn enable_mpu(&self) { let regs = unsafe { &*self.0 }; // Enable the MPU, disable it during HardFault/NMI handlers, allow // privileged code access to all unprotected memory. regs.control.set(0b101); let mpu_type = regs.mpu_type.get(); let regions = mpu_type.data_regions.get(); if regions != 8 { panic!( "Tock currently assumes 8 MPU regions. This chip has {}", regions ); } } fn disable_mpu(&self) { let regs = unsafe { &*self.0 }; regs.control.set(0b0); } fn create_region( region_num: usize, start: usize, len: usize, execute: kernel::mpu::ExecutePermission, access: kernel::mpu::AccessPermission, ) -> Option<Region> { if region_num >= 8 { // There are only 8 (0-indexed) regions available return None; } // There are two possibilities we support: // // 1. The base address is aligned exactly to the size of the region, // which uses an MPU region with the exact base address and size of // the memory region. // // 2. Otherwise, we can use a larger MPU region and expose only MPU // subregions, as long as the memory region's base address is aligned // to 1/8th of a larger region size. if start % len == 0 { // Memory base aligned to memory size - straight forward case let region_len = PowerOfTwo::floor(len as u32); if region_len.exp::<u32>() < 5 { // Region sizes must be 32 Bytes or larger return None; } else if region_len.exp::<u32>() > 32 { // Region sizes must be 4GB or smaller return None; } let xn = execute as u32; let ap = access as u32; Some(unsafe { Region::new( (start | 1 << 4 | (region_num & 0xf)) as u32, 1 | (region_len.exp::<u32>() - 1) << 1 | ap << 24 | xn << 28, ) }) } else { // Memory base not aligned to memory size // Which (power-of-two) subregion size would align with the base // address? // // We find this by taking smallest binary substring of the base // address with exactly one bit: // // 1 << (start.trailing_zeros()) let subregion_size = { let tz = start.trailing_zeros(); // `start` should never be 0 because of that's taken care of by // the previous branch, but in case it is, do the right thing // anyway. if tz < 32 { (1 as usize) << tz } else { 0 } }; // Once we have a subregion size, we get a region size by // multiplying it by the number of subregions per region. let region_size = subregion_size * 8; // Finally, we calculate the region base by finding the nearest // address below `start` that aligns with the region size. let region_start = start - (start % region_size); if region_size + region_start - start < len { // Sanity check that the amount left over space in the region // after `start` is at least as large as the memory region we // want to reference. return None; } if len % subregion_size != 0 { // Sanity check that there is some integer X such that // subregion_size * X == len so none of `len` is left over when // we take the max_subregion. return None; } // The index of the first subregion to activate is the number of // regions between `region_start` (MPU) and `start` (memory). let min_subregion = (start - region_start) / subregion_size; // The index of the last subregion to activate is the number of // regions that fit in `len`, plus the `min_subregion`, minus one // (because subregions are zero-indexed). let max_subregion = min_subregion + len / subregion_size - 1; let region_len = PowerOfTwo::floor(region_size as u32); if region_len.exp::<u32>() < 7 { // Subregions only supported for regions sizes 128 bytes and up. return None; } else if region_len.exp::<u32>() > 32 { // Region sizes must be 4GB or smaller return None; } // Turn the min/max subregion into a bitfield where all bits are `1` // except for the bits whose index lie within // [min_subregion, max_subregion] // // Note: Rust ranges are minimum inclusive, maximum exclusive, hence // max_subregion + 1. let subregion_mask = (min_subregion..(max_subregion + 1)).fold(!0, |res, i| res & !(1 << i)) & 0xff; let xn = execute as u32; let ap = access as u32; Some(unsafe { Region::new( (region_start | 1 << 4 | (region_num & 0xf)) as u32, 1 | subregion_mask << 8 | (region_len.exp::<u32>() - 1) << 1 | ap << 24 | xn << 28, ) }) } } fn set_mpu(&self, region: Region) { let regs = unsafe { &*self.0 }; regs.region_base_address.set(region.base_address()); regs.region_attributes_and_size.set(region.attributes()); } }