summaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorShawn Nematbakhsh <shawnn@chromium.org>2017-11-07 16:11:03 -0800
committerchrome-bot <chrome-bot@chromium.org>2017-11-14 10:11:18 -0800
commitb6991dd96d8bf6cb86a39b3da590ccd8b4e1e036 (patch)
treeb4a83fa1cb9f38bd08c76aa5f56b3e2c12c721a5 /core
parent2a62a3dfca91a3d8f755c1cf31fb3289f1511af3 (diff)
downloadchrome-ec-b6991dd96d8bf6cb86a39b3da590ccd8b4e1e036.tar.gz
cortex-m: mpu: Support unaligned regions and protect code RAM
Support protection of regions that aren't aligned to a power of 2 by using two MPU entries, and taking advantage of the sub-region feature. Also protect code RAM from being overwritten, on parts that use external storage. BUG=chromium:782244 BRANCH=None TEST=On kevin, call: mpu_protect_data_ram(); mpu_protect_code_ram(); mpu_enable(); Verify that first call results in the following update_region params: addr: 0x200c2000 size: 0xc01d Decoded: Protect 24K region Verify that second call results in the following params: addr: 0x100a8000 size: 0xc021 Decoded: Protect 96K region addr: 0x100c0000 size: 0xf01b Decoded: Protect remaining 8K region Also verify that writes to beginning and end of code ram region trigger data access violation after enabling protection. Also verify that sysjump fails. Change-Id: Ieb7a4ec3a089e8a2d29f231e1e3acf2e78e560a1 Signed-off-by: Shawn Nematbakhsh <shawnn@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/757721 Commit-Ready: Shawn N <shawnn@chromium.org> Tested-by: Shawn N <shawnn@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
Diffstat (limited to 'core')
-rw-r--r--core/cortex-m/include/mpu.h29
-rw-r--r--core/cortex-m/mpu.c140
2 files changed, 123 insertions, 46 deletions
diff --git a/core/cortex-m/include/mpu.h b/core/cortex-m/include/mpu.h
index c0c595a367..bb5eaa4c7e 100644
--- a/core/cortex-m/include/mpu.h
+++ b/core/cortex-m/include/mpu.h
@@ -10,6 +10,26 @@
#include "common.h"
+/*
+ * Region assignment. 7 as the highest, a higher index has a higher priority.
+ * For example, using 7 for .iram.text allows us to mark entire RAM XN except
+ * .iram.text, which is used for hibernation.
+ * Region assignment is currently wasteful and can be changed if more
+ * regions are needed in the future. For example, a second region may not
+ * be necessary for all types, and REGION_CODE_RAM / REGION_STORAGE can be
+ * made mutually exclusive.
+ */
+enum mpu_region {
+ REGION_DATA_RAM = 0, /* For internal data RAM */
+ REGION_DATA_RAM2 = 1, /* Second region for unaligned size */
+ REGION_CODE_RAM = 2, /* For internal code RAM */
+ REGION_CODE_RAM2 = 3, /* Second region for unaligned size */
+ REGION_STORAGE = 4, /* For mapped internal storage */
+ REGION_STORAGE2 = 5, /* Second region for unaligned size */
+ REGION_DATA_RAM_TEXT = 6, /* Exempt region of data RAM */
+ REGION_CHIP_RESERVED = 7, /* Reserved for use in chip/ */
+};
+
#define MPU_TYPE REG32(0xe000ed90)
#define MPU_CTRL REG32(0xe000ed94)
#define MPU_NUMBER REG32(0xe000ed98)
@@ -60,10 +80,15 @@ extern char __iram_text_end;
/**
* Protect RAM from code execution
*/
-int mpu_protect_ram(void);
+int mpu_protect_data_ram(void);
+
+/**
+ * Protect code RAM from being overwritten
+ */
+int mpu_protect_code_ram(void);
/**
- * Protect flash memory from code execution
+ * Protect internal mapped flash memory from code execution
*/
int mpu_lock_ro_flash(void);
int mpu_lock_rw_flash(void);
diff --git a/core/cortex-m/mpu.c b/core/cortex-m/mpu.c
index d12191dbdd..5537d1830d 100644
--- a/core/cortex-m/mpu.c
+++ b/core/cortex-m/mpu.c
@@ -11,15 +11,6 @@
#include "task.h"
#include "util.h"
-/* Region assignment. 7 as the highest, a higher index has a higher priority.
- * For example, using 7 for .iram.text allows us to mark entire RAM XN except
- * .iram.text, which is used for hibernation. */
-enum mpu_region {
- REGION_IRAM = 0, /* For internal RAM */
- REGION_FLASH_MEMORY = 1, /* For flash memory */
- REGION_IRAM_TEXT = 7 /* For *.(iram.text) */
-};
-
/**
* Update a memory region.
*
@@ -28,11 +19,12 @@ enum mpu_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
*/
static void mpu_update_region(uint8_t region, uint32_t addr, uint8_t size_bit,
- uint16_t attr, uint8_t enable)
+ uint16_t attr, uint8_t enable, uint8_t srd)
{
asm volatile("isb; dsb;");
@@ -41,7 +33,7 @@ static void mpu_update_region(uint8_t region, uint32_t addr, uint8_t size_bit,
if (enable) {
MPU_BASE = addr;
MPU_ATTR = attr;
- MPU_SIZE = (size_bit - 1) << 1 | 1; /* Enable */
+ MPU_SIZE = (srd << 8) | ((size_bit - 1) << 1) | 1; /* Enable */
}
asm volatile("isb; dsb;");
@@ -56,42 +48,74 @@ static void mpu_update_region(uint8_t region, uint32_t addr, uint8_t size_bit,
* 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 or EC_ERROR_INVAL if a parameter is invalid.
+ * Returns EC_SUCCESS on success or -EC_ERROR_INVAL if a parameter is invalid.
*/
static int mpu_config_region(uint8_t region, uint32_t addr, uint32_t size,
uint16_t attr, uint8_t enable)
{
int size_bit = 0;
+ uint8_t blocks, srd1, srd2;
if (!size)
return EC_SUCCESS;
- while (!(size & 1)) {
- size_bit++;
- size >>= 1;
+
+ /* Bit position of first '1' in size */
+ size_bit = 31 - __builtin_clz(size);
+ /* Min. region size is 32 bytes */
+ if (size_bit < 5)
+ return -EC_ERROR_INVAL;
+
+ /* If size is a power of 2 then represent it with a single MPU region */
+ if (POWER_OF_TWO(size)) {
+ mpu_update_region(region, addr, size_bit, attr, enable, 0);
+ return EC_SUCCESS;
}
- /* Region size must be a power of 2 (size == 0) and equal or larger than
- * 32 (size_bit >= 5) */
- if (size > 1 || size_bit < 5)
+
+ /* Sub-regions are not supported for region <= 128 bytes */
+ if (size_bit < 7)
+ return -EC_ERROR_INVAL;
+ /* Verify we can represent range with <= 2 regions */
+ if (size & ~(0x3f << (size_bit - 5)))
return -EC_ERROR_INVAL;
- mpu_update_region(region, addr, size_bit, attr, enable);
+ /*
+ * Round up size of first region to power of 2.
+ * Calculate the number of fully occupied blocks (block size =
+ * region size / 8) in the first region.
+ */
+ blocks = size >> (size_bit - 2);
+
+ /* Represent occupied blocks of two regions with srd mask. */
+ srd1 = (1 << blocks) - 1;
+ srd2 = (1 << ((size >> (size_bit - 5)) & 0x7)) - 1;
+
+ /*
+ * Second region not supported for DATA_RAM_TEXT, also verify size of
+ * second region is sufficient to support sub-regions.
+ */
+ if (srd2 && (region == REGION_DATA_RAM_TEXT || size_bit < 10))
+ return -EC_ERROR_INVAL;
- return EC_SUCCESS;
-}
+ /* Write first region. */
+ mpu_update_region(region, addr, size_bit + 1, attr, enable, ~srd1);
+
+ /*
+ * Second protection region (if necessary) begins at the first block
+ * we marked unoccupied in the first region.
+ * Size of the second region is the block size of first region.
+ */
+ addr += (1 << (size_bit - 2)) * blocks;
+
+ /*
+ * Now represent occupied blocks in the second region. It's possible
+ * that the first region completely represented the occupied area, if
+ * so then no second protection region is required.
+ */
+ if (srd2)
+ mpu_update_region(region + 1, addr, size_bit - 2, attr, enable,
+ ~srd2);
-/**
- * Set a region non-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_lock_region(uint8_t region, uint32_t addr, uint32_t size,
- uint8_t texscb)
-{
- return mpu_config_region(region, addr, size,
- MPU_ATTR_XN | MPU_ATTR_RW_RW | texscb, 1);
+ return EC_SUCCESS;
}
/**
@@ -124,31 +148,59 @@ uint32_t mpu_get_type(void)
return MPU_TYPE;
}
-int mpu_protect_ram(void)
+int mpu_protect_data_ram(void)
{
int ret;
- ret = mpu_lock_region(REGION_IRAM, CONFIG_RAM_BASE,
- CONFIG_DATA_RAM_SIZE, MPU_ATTR_INTERNAL_SRAM);
+
+ /* 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;
- ret = mpu_unlock_region(
- REGION_IRAM_TEXT, (uint32_t)&__iram_text_start,
+
+ /* 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);
- return ret;
}
+#ifdef CONFIG_EXTERNAL_STORAGE
+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_RO_SIZE,
+ MPU_ATTR_RO_NO | MPU_ATTR_INTERNAL_SRAM,
+ 1);
+}
+#else
int mpu_lock_ro_flash(void)
{
- return mpu_lock_region(REGION_FLASH_MEMORY, CONFIG_RO_MEM_OFF,
- CONFIG_RO_SIZE, MPU_ATTR_FLASH_MEMORY);
+ /* 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);
}
int mpu_lock_rw_flash(void)
{
- return mpu_lock_region(REGION_FLASH_MEMORY, CONFIG_RW_MEM_OFF,
- CONFIG_RW_SIZE, MPU_ATTR_FLASH_MEMORY);
+ /* Prevent execution from internal mapped RW flash */
+ return mpu_config_region(REGION_STORAGE,
+ CONFIG_MAPPED_STORAGE_BASE + CONFIG_RW_MEM_OFF,
+ CONFIG_RW_SIZE,
+ MPU_ATTR_XN | MPU_ATTR_RW_RW |
+ MPU_ATTR_FLASH_MEMORY, 1);
}
+#endif /* !CONFIG_EXTERNAL_STORAGE */
int mpu_pre_init(void)
{