mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-30 02:20:48 +00:00
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>
This commit is contained in:
committed by
chrome-bot
parent
2a62a3dfca
commit
b6991dd96d
@@ -41,9 +41,9 @@
|
||||
#undef CONFIG_RAM_BASE
|
||||
#define CONFIG_RAM_BASE (0x200C0000 + RAM_SHIFT_SIZE)
|
||||
#undef CONFIG_RAM_SIZE
|
||||
#define CONFIG_RAM_SIZE (0x00008000 - 0x800 - RAM_SHIFT_SIZE)
|
||||
/* Region sizes are no longer a power of 2 so we can't enable MPU */
|
||||
#undef CONFIG_MPU
|
||||
#undef CONFIG_DATA_RAM_SIZE
|
||||
#define CONFIG_DATA_RAM_SIZE (0x00008000 - RAM_SHIFT_SIZE)
|
||||
#define CONFIG_RAM_SIZE (CONFIG_DATA_RAM_SIZE - 0x800)
|
||||
|
||||
/* Optional features */
|
||||
#define CONFIG_BOARD_VERSION
|
||||
|
||||
@@ -325,22 +325,33 @@ void system_disable_jump(void)
|
||||
#ifdef CONFIG_MPU
|
||||
if (system_is_locked()) {
|
||||
int ret;
|
||||
int enable_mpu = 0;
|
||||
enum system_image_copy_t copy;
|
||||
enum system_image_copy_t __attribute__((unused)) copy;
|
||||
|
||||
CPRINTS("MPU type: %08x", mpu_get_type());
|
||||
/*
|
||||
* Protect RAM from code execution
|
||||
* Protect data RAM from code execution
|
||||
*/
|
||||
ret = mpu_protect_ram();
|
||||
ret = mpu_protect_data_ram();
|
||||
if (ret == EC_SUCCESS) {
|
||||
enable_mpu = 1;
|
||||
CPRINTS("RAM locked. Exclusion %08x-%08x",
|
||||
CPRINTS("data RAM locked. Exclusion %08x-%08x",
|
||||
&__iram_text_start, &__iram_text_end);
|
||||
} else {
|
||||
CPRINTS("Failed to lock RAM (%d)", ret);
|
||||
CPRINTS("Failed to lock data RAM (%d)", ret);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_EXTERNAL_STORAGE
|
||||
/*
|
||||
* Protect code RAM from being overwritten
|
||||
*/
|
||||
ret = mpu_protect_code_ram();
|
||||
if (ret == EC_SUCCESS) {
|
||||
CPRINTS("code RAM locked.");
|
||||
} else {
|
||||
CPRINTS("Failed to lock code RAM (%d)", ret);
|
||||
return;
|
||||
}
|
||||
#else
|
||||
/*
|
||||
* Protect inactive image (ie. RO if running RW, vice versa)
|
||||
* from code execution.
|
||||
@@ -359,20 +370,21 @@ void system_disable_jump(void)
|
||||
ret = !EC_SUCCESS;
|
||||
}
|
||||
if (ret == EC_SUCCESS) {
|
||||
enable_mpu = 1;
|
||||
CPRINTS("%s image locked",
|
||||
system_image_copy_t_to_string(copy));
|
||||
} else {
|
||||
CPRINTS("Failed to lock %s image (%d)",
|
||||
system_image_copy_t_to_string(copy), ret);
|
||||
return;
|
||||
}
|
||||
#endif /* !CONFIG_EXTERNAL_STORAGE */
|
||||
|
||||
if (enable_mpu)
|
||||
mpu_enable();
|
||||
/* All regions were configured successfully, enable MPU */
|
||||
mpu_enable();
|
||||
} else {
|
||||
CPRINTS("System is unlocked. Skip MPU configuration");
|
||||
}
|
||||
#endif
|
||||
#endif /* CONFIG_MPU */
|
||||
}
|
||||
|
||||
test_mockable enum system_image_copy_t system_get_image_copy(void)
|
||||
|
||||
@@ -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 flash memory from code execution
|
||||
* Protect code RAM from being overwritten
|
||||
*/
|
||||
int mpu_protect_code_ram(void);
|
||||
|
||||
/**
|
||||
* Protect internal mapped flash memory from code execution
|
||||
*/
|
||||
int mpu_lock_ro_flash(void);
|
||||
int mpu_lock_rw_flash(void);
|
||||
|
||||
@@ -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,44 +48,76 @@ 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;
|
||||
}
|
||||
/* 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)
|
||||
|
||||
/* 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;
|
||||
|
||||
mpu_update_region(region, addr, size_bit, attr, enable);
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/* 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);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a region executable and read-write.
|
||||
*
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user