Loading...
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 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 | /* * Copyright (c) 2017 Linaro Limited. * * SPDX-License-Identifier: Apache-2.0 */ #include <device.h> #include <init.h> #include <kernel.h> #include <soc.h> #include <arch/arm/cortex_m/mpu/arm_core_mpu_dev.h> #include <arch/arm/cortex_m/mpu/arm_core_mpu.h> #include <linker/linker-defs.h> #define LOG_LEVEL CONFIG_MPU_LOG_LEVEL #include <logging/log.h> LOG_MODULE_DECLARE(mpu); /* * Global status variable holding the number of HW MPU region indices, which * have been reserved by the MPU driver to program the static (fixed) memory * regions. */ static u8_t static_regions_num; /** * Get the number of supported MPU regions. */ static inline u8_t get_num_regions(void) { #if defined(CONFIG_CPU_CORTEX_M0PLUS) || \ defined(CONFIG_CPU_CORTEX_M3) || \ defined(CONFIG_CPU_CORTEX_M4) /* Cortex-M0+, Cortex-M3, and Cortex-M4 MCUs may * have a fixed number of 8 MPU regions. */ return 8; #else u32_t type = MPU->TYPE; type = (type & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos; return (u8_t)type; #endif } /* Include architecture-specific internal headers. */ #if defined(CONFIG_CPU_CORTEX_M0PLUS) || \ defined(CONFIG_CPU_CORTEX_M3) || \ defined(CONFIG_CPU_CORTEX_M4) || \ defined(CONFIG_CPU_CORTEX_M7) #include <arm_mpu_v7_internal.h> #elif defined(CONFIG_CPU_CORTEX_M23) || \ defined(CONFIG_CPU_CORTEX_M33) #include <arm_mpu_v8_internal.h> #else #error "Unsupported ARM CPU" #endif static int region_allocate_and_init(const u8_t index, const struct arm_mpu_region *region_conf) { /* Attempt to allocate new region index. */ if (index > (get_num_regions() - 1)) { /* No available MPU region index. */ LOG_ERR("Failed to allocate new MPU region %u\n", index); return -EINVAL; } LOG_DBG("Program MPU region at index 0x%x", index); /* Program region */ region_init(index, region_conf); return index; } /* This internal function programs an MPU region * of a given configuration at a given MPU index. */ static int mpu_configure_region(const u8_t index, const struct k_mem_partition *new_region) { struct arm_mpu_region region_conf; LOG_DBG("Configure MPU region at index 0x%x", index); /* Populate internal ARM MPU region configuration structure. */ region_conf.base = new_region->start; get_region_attr_from_k_mem_partition_info(®ion_conf.attr, &new_region->attr, new_region->start, new_region->size); /* Allocate and program region */ return region_allocate_and_init(index, (const struct arm_mpu_region *)®ion_conf); } /* ARM Core MPU Driver API Implementation for ARM MPU */ /** * @brief enable the MPU */ void arm_core_mpu_enable(void) { /* Enable MPU and use the default memory map as a * background region for privileged software access. */ MPU->CTRL = MPU_CTRL_ENABLE_Msk | MPU_CTRL_PRIVDEFENA_Msk; /* Make sure that all the registers are set before proceeding */ __DSB(); __ISB(); } /** * @brief disable the MPU */ void arm_core_mpu_disable(void) { /* Force any outstanding transfers to complete before disabling MPU */ __DMB(); /* Disable MPU */ MPU->CTRL = 0; } #if defined(CONFIG_USERSPACE) /** * @brief update configuration of an active memory partition */ void arm_core_mpu_mem_partition_config_update( struct k_mem_partition *partition, k_mem_partition_attr_t *new_attr) { /* Find the partition. ASSERT if not found. */ u8_t i; u8_t reg_index = get_num_regions(); for (i = get_dyn_region_min_index(); i < get_num_regions(); i++) { if (!is_enabled_region(i)) { continue; } u32_t base = mpu_region_get_base(i); if (base != partition->start) { continue; } u32_t size = mpu_region_get_size(i); if (size != partition->size) { continue; } /* Region found */ reg_index = i; break; } __ASSERT(reg_index != get_num_regions(), "Memory domain partition not found\n"); /* Modify the permissions */ partition->attr = *new_attr; mpu_configure_region(reg_index, partition); } /** * @brief get the maximum number of available (free) MPU region indices * for configuring dynamic MPU partitions */ int arm_core_mpu_get_max_available_dyn_regions(void) { return get_num_regions() - static_regions_num; } /** * @brief validate the given buffer is user accessible or not * * Presumes the background mapping is NOT user accessible. */ int arm_core_mpu_buffer_validate(void *addr, size_t size, int write) { return mpu_buffer_validate(addr, size, write); } #endif /* CONFIG_USERSPACE */ /** * @brief configure fixed (static) MPU regions. */ void arm_core_mpu_configure_static_mpu_regions(const struct k_mem_partition *static_regions[], const u8_t regions_num, const u32_t background_area_start, const u32_t background_area_end) { if (mpu_configure_static_mpu_regions(static_regions, regions_num, background_area_start, background_area_end) == -EINVAL) { __ASSERT(0, "Configuring %u static MPU regions failed\n", regions_num); } } #if defined(CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS) /** * @brief mark memory areas for dynamic region configuration */ void arm_core_mpu_mark_areas_for_dynamic_regions( const struct k_mem_partition dyn_region_areas[], const u8_t dyn_region_areas_num) { if (mpu_mark_areas_for_dynamic_regions(dyn_region_areas, dyn_region_areas_num) == -EINVAL) { __ASSERT(0, "Marking %u areas for dynamic regions failed\n", dyn_region_areas_num); } } #endif /* CONFIG_MPU_REQUIRES_NON_OVERLAPPING_REGIONS */ /** * @brief configure dynamic MPU regions. */ void arm_core_mpu_configure_dynamic_mpu_regions(const struct k_mem_partition *dynamic_regions[], u8_t regions_num) { if (mpu_configure_dynamic_mpu_regions(dynamic_regions, regions_num) == -EINVAL) { __ASSERT(0, "Configuring %u dynamic MPU regions failed\n", regions_num); } } /* ARM MPU Driver Initial Setup */ /* * @brief MPU default configuration * * This function provides the default configuration mechanism for the Memory * Protection Unit (MPU). */ static int arm_mpu_init(struct device *arg) { u32_t r_index; if (mpu_config.num_regions > get_num_regions()) { /* Attempt to configure more MPU regions than * what is supported by hardware. As this operation * is executed during system (pre-kernel) initialization, * we want to ensure we can detect an attempt to * perform invalid configuration. */ __ASSERT(0, "Request to configure: %u regions (supported: %u)\n", mpu_config.num_regions, get_num_regions() ); return -1; } LOG_DBG("total region count: %d", get_num_regions()); arm_core_mpu_disable(); /* Architecture-specific configuration */ mpu_init(); /* Program fixed regions configured at SOC definition. */ for (r_index = 0U; r_index < mpu_config.num_regions; r_index++) { region_init(r_index, &mpu_config.mpu_regions[r_index]); } /* Update the number of programmed MPU regions. */ static_regions_num = mpu_config.num_regions; arm_core_mpu_enable(); /* Sanity check for number of regions in Cortex-M0+, M3, and M4. */ #if defined(CONFIG_CPU_CORTEX_M0PLUS) || \ defined(CONFIG_CPU_CORTEX_M3) || \ defined(CONFIG_CPU_CORTEX_M4) __ASSERT( (MPU->TYPE & MPU_TYPE_DREGION_Msk) >> MPU_TYPE_DREGION_Pos == 8, "Invalid number of MPU regions\n"); #endif return 0; } SYS_INIT(arm_mpu_init, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); |