diff options
Diffstat (limited to 'core/cortex-m/mpu.c')
-rw-r--r-- | core/cortex-m/mpu.c | 457 |
1 files changed, 0 insertions, 457 deletions
diff --git a/core/cortex-m/mpu.c b/core/cortex-m/mpu.c deleted file mode 100644 index 29da931a28..0000000000 --- a/core/cortex-m/mpu.c +++ /dev/null @@ -1,457 +0,0 @@ -/* Copyright 2013 The Chromium OS Authors. All rights reserved. - * Use of this source code is governed by a BSD-style license that can be - * found in the LICENSE file. - */ - -/* MPU module for Chrome EC */ - -#include "mpu.h" -#include "console.h" -#include "cpu.h" -#include "registers.h" -#include "task.h" -#include "util.h" - -/** - * @return Number of regions supported by the MPU. 0 means the processor does - * not implement an MPU. - */ -int mpu_num_regions(void) -{ - return MPU_TYPE_REG_COUNT(mpu_get_type()); -} - -/** - * @return true if processor has MPU, false otherwise - */ -bool has_mpu(void) -{ - return mpu_num_regions() != 0; -} - -/** - * @return true if MPU has unified instruction and data maps, false otherwise - */ -bool mpu_is_unified(void) -{ - return (mpu_get_type() & MPU_TYPE_UNIFIED_MASK) == 0; -} - - -/** - * Update a memory region. - * - * region: index of the region to update - * addr: base address of the region - * size_bit: size of the region in power of two. - * attr: attribute bits. Current value will be overwritten if enable is true. - * enable: enables the region if non zero. Otherwise, disables the region. - * srd: subregion mask to partition region into 1/8ths, 0 = subregion enabled. - * - * Based on 3.1.4.1 'Updating an MPU Region' of Stellaris LM4F232H5QC Datasheet - */ -int mpu_update_region(uint8_t region, uint32_t addr, uint8_t size_bit, - uint16_t attr, uint8_t enable, uint8_t srd) -{ - /* - * Note that on the Cortex-M3, Cortex-M4, and Cortex-M7, the base - * address used for an MPU region must be aligned to the size of the - * region: - * - * https://developer.arm.com/docs/dui0553/a/cortex-m4-peripherals/optional-memory-protection-unit/mpu-region-base-address-register - * https://developer.arm.com/docs/dui0552/a/cortex-m3-peripherals/optional-memory-protection-unit/mpu-region-base-address-register - * https://developer.arm.com/docs/dui0646/a/cortex-m7-peripherals/optional-memory-protection-unit/mpu-region-base-address-register#BABDAHJG - */ - if (!is_aligned(addr, BIT(size_bit))) - return -EC_ERROR_INVAL; - - if (region >= mpu_num_regions()) - return -EC_ERROR_INVAL; - - if (size_bit < MPU_SIZE_BITS_MIN) - return -EC_ERROR_INVAL; - - asm volatile("isb; dsb;"); - - MPU_NUMBER = region; - MPU_SIZE &= ~1; /* Disable */ - if (enable) { - MPU_BASE = addr; - /* - * MPU_ATTR = attr; - * MPU_SIZE = (srd << 8) | ((size_bit - 1) << 1) | 1; - * - * WORKAROUND: the 2 half-word accesses above should work - * according to the doc, but they don't ..., do a single 32-bit - * one. - */ - REG32(&MPU_SIZE) = ((uint32_t)attr << 16) - | (srd << 8) | ((size_bit - 1) << 1) | 1; - } - - asm volatile("isb; dsb;"); - - return EC_SUCCESS; -} - -/* - * Greedily configure the largest possible part of the given region from the - * base address. - * - * Returns EC_SUCCESS on success and sets *consumed to the number of bytes - * mapped from the base address. In case of error, the value of *consumed is - * unpredictable. - * - * For instance, if addr is 0x10070000 and size is 0x30000 then memory in the - * range 0x10070000-0x10080000 will be configured and *consumed will be set to - * 0x10000. - */ -static int mpu_config_region_greedy(uint8_t region, uint32_t addr, - uint32_t size, uint16_t attr, - uint8_t enable, uint32_t *consumed) -{ - /* - * Compute candidate alignment to be used for the MPU region. - * - * This is the minimum of the base address and size alignment, since - * regions must be naturally aligned to their size. - */ - uint8_t natural_alignment = MIN(addr == 0 ? 32 : alignment_log2(addr), - alignment_log2(size)); - uint8_t subregion_disable = 0; - - if (natural_alignment >= 5) { - int sr_idx; - uint32_t subregion_base, subregion_size; - /* - * For MPU regions larger than 256 bytes we can use subregions, - * (which are a minimum of 32 bytes in size) making the actual - * MPU region 8x larger. Depending on the address alignment this - * can allow us to cover a larger area (and never a smaller - * one). - */ - natural_alignment += 3; - /* Region size cannot exceed 4GB. */ - if (natural_alignment > 32) - natural_alignment = 32; - - /* - * Generate the subregion mask by walking through each, - * disabling if it is not completely contained in the requested - * range. - */ - subregion_base = addr & ~((1 << natural_alignment) - 1); - subregion_size = 1 << (natural_alignment - 3); - *consumed = 0; - for (sr_idx = 0; sr_idx < 8; sr_idx++) { - if (subregion_base < addr || - (subregion_base + subregion_size) > (addr + size)) - /* lsb of subregion mask is lowest address */ - subregion_disable |= 1 << sr_idx; - else - /* not disabled means consumed */ - *consumed += subregion_size; - - subregion_base += subregion_size; - } - } else { - /* Not using subregions; all enabled */ - *consumed = 1 << natural_alignment; - } - - return mpu_update_region(region, - addr & ~((1 << natural_alignment) - 1), - natural_alignment, - attr, enable, subregion_disable); -} - -/** - * Configure a region - * - * region: index of the region to update - * addr: Base address of the region - * size: Size of the region in bytes - * attr: Attribute bits. Current value will be overwritten if enable is set. - * enable: Enables the region if non zero. Otherwise, disables the region. - * - * Returns EC_SUCCESS on success, -EC_ERROR_OVERFLOW if it is not possible to - * fully configure the given region, or -EC_ERROR_INVAL if a parameter is - * invalid (such as the address or size having unsupported alignment). - */ -int mpu_config_region(uint8_t region, uint32_t addr, uint32_t size, - uint16_t attr, uint8_t enable) -{ - int rv; - uint32_t consumed; - - /* Zero size doesn't require configuration */ - if (size == 0) - return EC_SUCCESS; - - rv = mpu_config_region_greedy(region, addr, size, - attr, enable, &consumed); - if (rv != EC_SUCCESS) - return rv; - ASSERT(consumed <= size); - addr += consumed; - size -= consumed; - - /* Regions other than DATA_RAM_TEXT may use two MPU regions */ - if (size > 0 && region != REGION_DATA_RAM_TEXT) { - rv = mpu_config_region_greedy(region + 1, addr, size, - attr, enable, &consumed); - if (rv != EC_SUCCESS) - return rv; - ASSERT(consumed <= size); - addr += consumed; - size -= consumed; - } - - if (size > 0) - return EC_ERROR_OVERFLOW; - return EC_SUCCESS; -} - -/** - * Set a region executable and read-write. - * - * region: index of the region - * addr: base address of the region - * size: size of the region in bytes - * texscb: TEX and SCB bit field - */ -static int mpu_unlock_region(uint8_t region, uint32_t addr, uint32_t size, - uint8_t texscb) -{ - return mpu_config_region(region, addr, size, - MPU_ATTR_RW_RW | texscb, 1); -} - -void mpu_enable(void) -{ - MPU_CTRL |= MPU_CTRL_PRIVDEFEN | MPU_CTRL_HFNMIENA | MPU_CTRL_ENABLE; -} - -void mpu_disable(void) -{ - MPU_CTRL &= ~(MPU_CTRL_PRIVDEFEN | MPU_CTRL_HFNMIENA | MPU_CTRL_ENABLE); -} - -uint32_t mpu_get_type(void) -{ - return MPU_TYPE; -} - -int mpu_protect_data_ram(void) -{ - int ret; - - /* Prevent code execution from data RAM */ - ret = mpu_config_region(REGION_DATA_RAM, - CONFIG_RAM_BASE, - CONFIG_DATA_RAM_SIZE, - MPU_ATTR_XN | - MPU_ATTR_RW_RW | - MPU_ATTR_INTERNAL_SRAM, - 1); - if (ret != EC_SUCCESS) - return ret; - - /* Exempt the __iram_text section */ - return mpu_unlock_region( - REGION_DATA_RAM_TEXT, (uint32_t)&__iram_text_start, - (uint32_t)(&__iram_text_end - &__iram_text_start), - MPU_ATTR_INTERNAL_SRAM); -} - -#if defined(CONFIG_EXTERNAL_STORAGE) || !defined(CONFIG_FLASH_PHYSICAL) -int mpu_protect_code_ram(void) -{ - /* Prevent write access to code RAM */ - return mpu_config_region(REGION_STORAGE, - CONFIG_PROGRAM_MEMORY_BASE + CONFIG_RO_MEM_OFF, - CONFIG_CODE_RAM_SIZE, - MPU_ATTR_RO_NO | MPU_ATTR_INTERNAL_SRAM, - 1); -} -#else -int mpu_lock_ro_flash(void) -{ - /* Prevent execution from internal mapped RO flash */ - return mpu_config_region(REGION_STORAGE, - CONFIG_MAPPED_STORAGE_BASE + CONFIG_RO_MEM_OFF, - CONFIG_RO_SIZE, - MPU_ATTR_XN | MPU_ATTR_RW_RW | - MPU_ATTR_FLASH_MEMORY, 1); -} - -/* Represent RW with at most 2 MPU regions. */ -struct mpu_rw_regions mpu_get_rw_regions(void) -{ - int aligned_size_bit; - struct mpu_rw_regions regions = {}; - - regions.addr[0] = CONFIG_MAPPED_STORAGE_BASE + CONFIG_RW_MEM_OFF; - - /* - * Least significant set bit of the address determines the max size of - * the region because on the Cortex-M3, Cortex-M4 and Cortex-M7, the - * address used for an MPU region must be aligned to the size. - */ - aligned_size_bit = - __fls(regions.addr[0] & -regions.addr[0]); - regions.size[0] = MIN(BIT(aligned_size_bit), CONFIG_RW_SIZE); - regions.addr[1] = regions.addr[0] + regions.size[0]; - regions.size[1] = CONFIG_RW_SIZE - regions.size[0]; - regions.num_regions = (regions.size[1] == 0) ? 1 : 2; - - return regions; -} - -int mpu_lock_rw_flash(void) -{ - /* Prevent execution from internal mapped RW flash */ - const uint16_t mpu_attr = MPU_ATTR_XN | MPU_ATTR_RW_RW | - MPU_ATTR_FLASH_MEMORY; - const struct mpu_rw_regions regions = mpu_get_rw_regions(); - int rv; - - rv = mpu_config_region(REGION_STORAGE, regions.addr[0], regions.size[0], - mpu_attr, 1); - if ((rv != EC_SUCCESS) || (regions.num_regions == 1)) - return rv; - - /* If this fails then it's impossible to represent with two regions. */ - return mpu_config_region(REGION_STORAGE2, regions.addr[1], - regions.size[1], mpu_attr, 1); -} -#endif /* !CONFIG_EXTERNAL_STORAGE */ - -#ifdef CONFIG_ROLLBACK_MPU_PROTECT -int mpu_lock_rollback(int lock) -{ - int rv; - int num_mpu_regions = mpu_num_regions(); - - const uint32_t rollback_region_start_address = - CONFIG_MAPPED_STORAGE_BASE + CONFIG_ROLLBACK_OFF; - const uint32_t rollback_region_total_size = CONFIG_ROLLBACK_SIZE; - const uint16_t mpu_attr = - MPU_ATTR_XN /* Execute never */ | - MPU_ATTR_NO_NO /* No access (privileged or unprivileged */; - - /* - * Originally rollback MPU support was added on Cortex-M7, which - * supports 16 MPU regions and has rollback region aligned in a way - * that we can use a single region. - */ - uint8_t rollback_mpu_region = REGION_ROLLBACK; - - if (rollback_mpu_region < num_mpu_regions) { - rv = mpu_config_region(rollback_mpu_region, - rollback_region_start_address, - rollback_region_total_size, mpu_attr, - lock); - return rv; - } - - /* - * If we get here, we can't use REGION_ROLLBACK because our MPU doesn't - * have enough regions. Instead, we choose unused MPU regions. - * - * Note that on the Cortex-M3, Cortex-M4, and Cortex-M7, the base - * address used for an MPU region must be aligned to the size of the - * region, so it's not possible to use a single region to protect the - * entire rollback flash on the STM32F412 (bloonchipper); we have to - * use two. - * - * See mpu_update_region for alignment details. - */ - - rollback_mpu_region = REGION_CHIP_RESERVED; - rv = mpu_config_region(rollback_mpu_region, - rollback_region_start_address, - rollback_region_total_size / 2, mpu_attr, lock); - if (rv != EC_SUCCESS) - return rv; - - rollback_mpu_region = REGION_CODE_RAM; - rv = mpu_config_region(rollback_mpu_region, - rollback_region_start_address + - (rollback_region_total_size / 2), - rollback_region_total_size / 2, mpu_attr, lock); - return rv; -} -#endif - -#ifdef CONFIG_CHIP_UNCACHED_REGION -/* Store temporarily the regions ranges to use them for the MPU configuration */ -#define REGION(_name, _flag, _start, _size) \ - static const uint32_t CONCAT2(_region_start_, _name) \ - __attribute__((unused, section(".unused"))) = _start; \ - static const uint32_t CONCAT2(_region_size_, _name) \ - __attribute__((unused, section(".unused"))) = _size; -#include "memory_regions.inc" -#undef REGION -#endif /* CONFIG_CHIP_UNCACHED_REGION */ - -int mpu_pre_init(void) -{ - int i; - int num_mpu_regions; - int rv; - - if (!has_mpu()) - return EC_ERROR_HW_INTERNAL; - - num_mpu_regions = mpu_num_regions(); - - /* Supports MPU with 8 or 16 unified regions */ - if (!mpu_is_unified() || - (num_mpu_regions != 8 && num_mpu_regions != 16)) - return EC_ERROR_UNIMPLEMENTED; - - mpu_disable(); - - for (i = 0; i < num_mpu_regions; ++i) { - /* - * Disable all regions. - * - * We use the smallest possible size (32 bytes), but it - * doesn't really matter since the regions are disabled. - * - * Use the fixed SRAM region base to ensure base is aligned - * to the region size. - */ - rv = mpu_update_region(i, CORTEX_M_SRAM_BASE, MPU_SIZE_BITS_MIN, - 0, 0, 0); - if (rv != EC_SUCCESS) - return rv; - } - - if (IS_ENABLED(CONFIG_ROLLBACK_MPU_PROTECT)) { - rv = mpu_lock_rollback(1); - if (rv != EC_SUCCESS) - return rv; - } - - if (IS_ENABLED(CONFIG_ARMV7M_CACHE)) { -#ifdef CONFIG_CHIP_UNCACHED_REGION - rv = mpu_config_region( - REGION_UNCACHED_RAM, - CONCAT2(_region_start_, CONFIG_CHIP_UNCACHED_REGION), - CONCAT2(_region_size_, CONFIG_CHIP_UNCACHED_REGION), - MPU_ATTR_XN | MPU_ATTR_RW_RW, 1); - if (rv != EC_SUCCESS) - return rv; - -#endif - } - - mpu_enable(); - - if (IS_ENABLED(CONFIG_ARMV7M_CACHE)) - cpu_enable_caches(); - - return EC_SUCCESS; -} |