summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/lm4/config_chip.h1
-rw-r--r--common/main.c7
-rw-r--r--common/system_common.c21
-rw-r--r--core/cortex-m/build.mk1
-rw-r--r--core/cortex-m/ec.lds.S11
-rw-r--r--core/cortex-m/include/mpu.h84
-rw-r--r--core/cortex-m/mpu.c121
7 files changed, 246 insertions, 0 deletions
diff --git a/chip/lm4/config_chip.h b/chip/lm4/config_chip.h
index 9fbda8d970..1ade3365f4 100644
--- a/chip/lm4/config_chip.h
+++ b/chip/lm4/config_chip.h
@@ -117,6 +117,7 @@
#define CONFIG_LPC
#define CONFIG_PECI
#define CONFIG_SWITCH
+#define CONFIG_MPU
/* Compile for running from RAM instead of flash */
/* #define COMPILE_FOR_RAM */
diff --git a/common/main.c b/common/main.c
index 9a5a75f1cd..ec4bc2b48c 100644
--- a/common/main.c
+++ b/common/main.c
@@ -17,6 +17,9 @@
#include "hooks.h"
#include "jtag.h"
#include "keyboard_scan.h"
+#ifdef CONFIG_MPU
+#include "mpu.h"
+#endif
#include "system.h"
#include "task.h"
#include "timer.h"
@@ -40,6 +43,10 @@ test_mockable int main(void)
board_config_pre_init();
#endif
+#ifdef CONFIG_MPU
+ mpu_pre_init();
+#endif
+
/* Configure the pin multiplexers and GPIOs */
jtag_pre_init();
gpio_pre_init();
diff --git a/common/system_common.c b/common/system_common.c
index ecf72c0782..55ea35d20d 100644
--- a/common/system_common.c
+++ b/common/system_common.c
@@ -13,6 +13,9 @@
#include "hooks.h"
#include "host_command.h"
#include "lpc.h"
+#ifdef CONFIG_MPU
+#include "mpu.h"
+#endif
#include "panic.h"
#include "system.h"
#include "task.h"
@@ -212,6 +215,24 @@ const uint8_t *system_get_jump_tag(uint16_t tag, int *version, int *size)
void system_disable_jump(void)
{
disable_jump = 1;
+
+#ifdef CONFIG_MPU
+ /*
+ * Lock down memory
+ * TODO: Lock down other images (RO or RW) not running.
+ */
+ {
+ int mpu_error = mpu_protect_ram();
+ if (mpu_error == EC_SUCCESS) {
+ mpu_enable();
+ CPRINTF("RAM locked. Exclusion %08x-%08x\n",
+ &__iram_text_start, &__iram_text_end);
+ } else {
+ CPRINTF("Failed to lock RAM. mpu_type:%08x. error:%d\n",
+ mpu_get_type(), mpu_error);
+ }
+ }
+#endif
}
test_mockable enum system_image_copy_t system_get_image_copy(void)
diff --git a/core/cortex-m/build.mk b/core/cortex-m/build.mk
index 5a7877bd02..5f036bc522 100644
--- a/core/cortex-m/build.mk
+++ b/core/cortex-m/build.mk
@@ -16,3 +16,4 @@ CFLAGS_CPU+=$(CFLAGS_FPU-y)
core-y=cpu.o init.o panic.o switch.o task.o timer.o
core-$(CONFIG_WATCHDOG)+=watchdog.o
+core-$(CONFIG_MPU)+=mpu.o
diff --git a/core/cortex-m/ec.lds.S b/core/cortex-m/ec.lds.S
index 115310bc28..6d858abd15 100644
--- a/core/cortex-m/ec.lds.S
+++ b/core/cortex-m/ec.lds.S
@@ -162,9 +162,20 @@ SECTIONS
__data_start = .;
*(.data.tasks)
*(.data)
+#ifdef CONFIG_MPU
+ /* It has to be aligned by 32 bytes to be a valid MPU region. */
+ . = ALIGN(32);
+ __iram_text_start = .;
+#else
. = ALIGN(4);
+#endif
*(.iram.text)
+#ifdef CONFIG_MPU
+ . = ALIGN(32);
+ __iram_text_end = .;
+#else
. = ALIGN(4);
+#endif
__data_end = .;
/* Shared memory buffer must be at the end of preallocated RAM, so it
diff --git a/core/cortex-m/include/mpu.h b/core/cortex-m/include/mpu.h
new file mode 100644
index 0000000000..c3a632aff7
--- /dev/null
+++ b/core/cortex-m/include/mpu.h
@@ -0,0 +1,84 @@
+/* Copyright (c) 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 Cortex-M3 */
+
+#ifndef __CROS_EC_MPU_H
+#define __CROS_EC_MPU_H
+
+#include "common.h"
+
+#define MPU_TYPE REG32(0xe000ed90)
+#define MPU_CTRL REG32(0xe000ed94)
+#define MPU_NUMBER REG32(0xe000ed98)
+#define MPU_BASE REG32(0xe000ed9c)
+#define MPU_SIZE REG16(0xe000eda0)
+#define MPU_ATTR REG16(0xe000eda2)
+
+#define MPU_CTRL_PRIVDEFEN (1 << 2)
+#define MPU_CTRL_HFNMIENA (1 << 1)
+#define MPU_CTRL_ENABLE (1 << 0)
+#define MPU_ATTR_NX (1 << 12)
+#define MPU_ATTR_NOACCESS (0 << 8)
+#define MPU_ATTR_FULLACCESS (3 << 8)
+/* Suggested in table 3-6 of Stellaris LM4F232H5QC Datasheet and table 38 of
+ * STM32F10xxx Cortex-M3 programming manual for internal sram. */
+#define MPU_ATTR_INTERNALSRAM 6
+
+/**
+ * Configure a region
+ *
+ * region: Number of the region to update
+ * addr: Base address of the region
+ * size: Size of the region in bytes
+ * attr: Attribute of the region. 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.
+ */
+int mpu_config_region(uint8_t region, uint32_t addr, uint32_t size,
+ uint16_t attr, uint8_t enable);
+
+/**
+ * Set a region non-executable.
+ *
+ * region: number of the region
+ * addr: base address of the region
+ * size: size of the region in bytes
+ */
+int mpu_nx_region(uint8_t region, uint32_t addr, uint32_t size);
+
+/**
+ * Enable MPU */
+void mpu_enable(void);
+
+/**
+ * Returns the value of MPU type register
+ *
+ * Bit fields:
+ * [15:8] Number of the data regions implemented or 0 if MPU is not present.
+ * [1] 0: unified (no distinction between instruction and data)
+ * 1: separated
+ */
+uint32_t mpu_get_type(void);
+
+/* Location of iram.text */
+extern char __iram_text_start;
+extern char __iram_text_end;
+
+/**
+ * Lock down RAM
+ */
+int mpu_protect_ram(void);
+
+/**
+ * Initialize MPU.
+ * It disables all regions if MPU is implemented. Otherwise, returns
+ * EC_ERROR_UNIMPLEMENTED.
+ */
+int mpu_pre_init(void);
+
+#endif /* __CROS_EC_MPU_H */
diff --git a/core/cortex-m/mpu.c b/core/cortex-m/mpu.c
new file mode 100644
index 0000000000..23d434d7e6
--- /dev/null
+++ b/core/cortex-m/mpu.c
@@ -0,0 +1,121 @@
+/* Copyright (c) 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 "registers.h"
+#include "task.h"
+#include "util.h"
+
+/**
+ * Update a memory region.
+ *
+ * region: number of the region to update
+ * addr: base address of the region
+ * size_bit: size of the region in power of two.
+ * attr: attribute of the region. Current value will be overwritten if enable
+ * is set.
+ * enable: enables the region if non zero. Otherwise, disables the region.
+ *
+ * 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)
+{
+ asm volatile("isb; dsb;");
+
+ MPU_NUMBER = region;
+ MPU_SIZE &= ~1; /* Disable */
+ if (enable) {
+ MPU_BASE = addr;
+ MPU_ATTR = attr;
+ MPU_SIZE = (size_bit - 1) << 1 | 1; /* Enable */
+ }
+
+ asm volatile("isb; dsb;");
+}
+
+int mpu_config_region(uint8_t region, uint32_t addr, uint32_t size,
+ uint16_t attr, uint8_t enable)
+{
+ int size_bit = 0;
+
+ if (!size)
+ return -EC_ERROR_INVAL;
+ 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)
+ return -EC_ERROR_INVAL;
+
+ mpu_update_region(region, addr, size_bit, attr, enable);
+
+ return EC_SUCCESS;
+}
+
+int mpu_nx_region(uint8_t region, uint32_t addr, uint32_t size)
+{
+ return mpu_config_region(
+ region, addr, size,
+ MPU_ATTR_NX | MPU_ATTR_FULLACCESS | MPU_ATTR_INTERNALSRAM, 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;
+}
+
+/**
+ * Prevent code from running on RAM.
+ * We need to allow execution from iram.text. Using higher region# allows us to
+ * do 'whitelisting' (lock down all then create exceptions).
+ */
+int mpu_protect_ram(void)
+{
+ int ret;
+
+ ret = mpu_nx_region(0, CONFIG_RAM_BASE, CONFIG_RAM_SIZE);
+ if (ret != EC_SUCCESS)
+ return ret;
+
+ ret = mpu_config_region(
+ 7, (uint32_t)&__iram_text_start,
+ (uint32_t)(&__iram_text_end - &__iram_text_start),
+ MPU_ATTR_FULLACCESS | MPU_ATTR_INTERNALSRAM, 1);
+
+ return ret;
+}
+
+int mpu_pre_init(void)
+{
+ int i;
+
+ if (mpu_get_type() != 0x00000800)
+ return EC_ERROR_UNIMPLEMENTED;
+
+ mpu_disable();
+ for (i = 0; i < 8; ++i) {
+ mpu_config_region(
+ i, CONFIG_RAM_BASE, CONFIG_RAM_SIZE,
+ MPU_ATTR_FULLACCESS | MPU_ATTR_INTERNALSRAM, 0);
+ }
+
+ return EC_SUCCESS;
+}