Protect inactive EC image from code execution

This change configures MPU to prevent instruction fetch from the flash image
that is not running at the time system_disable_jump is called. Violating
the protection causes instruction access violation, then the EC reboots.

RO image protection is tested as follows:
  ...
  [6.255696 MPU type: 00000800]
  [6.255874 RAM locked. Exclusion 20005680-200056a0]
  [6.256168 RO image locked]
  ...

  > sysjump 0
  Jumping to 0x00000000

  === PROCESS EXCEPTION: 03 ====== xPSR: 60000000 ===
  r0 :00000000 r1 :2000541c r2 :00001388 r3 :20007fe8
  r4 :200032f0 r5 :00000000 r6 :20002b70 r7 :20002df4
  r8 :0002d308 r9 :20002df4 r10:00000000 r11:00000000
  r12:00000002 sp :20002358 lr :0002a1a7 pc :00000000
  Instruction access violation, Forced hard fault
  mmfs = 1, shcsr = 70000, hfsr = 40000000, dfsr = 0

  =========== Process Stack Contents ===========
  200023c0: 00000098 00000000 00000000 0002a785
  200023d0: 00000002 20002dfd 00000007 20002b70
  200023e0: 00000002 00025777 00000000 20002dfd
  200023f0: 20002df4 20002dfc 00000000 00000000

  Rebooting...

Memory management fault status register has bit0 set, indicating there was an
instruction fetch volation. FYI, RAM protection is still working:

  > sysjump 0x20000000
  Jumping to 0x20000000

  === PROCESS EXCEPTION: 03 ====== xPSR: 60000000 ===
  r0 :00000000 r1 :2000541c r2 :00001388 r3 :20007fe8
  r4 :200032f0 r5 :20000000 r6 :20002b70 r7 :20002df4
  r8 :0002d308 r9 :20002df4 r10:00000000 r11:00000000
  r12:00000002 sp :20002358 lr :0002a1a7 pc :20000000
  Instruction access violation, Forced hard fault
  mmfs = 1, shcsr = 70000, hfsr = 40000000, dfsr = 0

  =========== Process Stack Contents ===========
  200023c0: 00000098 00000000 20000000 0002a785
  200023d0: 00000002 20002e06 00000007 20002b70
  200023e0: 00000002 00025777 00000000 20002e06
  200023f0: 20002df4 20002dfc 00000000 00000000

  Rebooting...

TEST=Booted Peppy. Tested lid close & open. Ran Flashrom from userspace to
update main firmware then software-synched an EC image.
BUG=chrome-os-partner:16904
BRANCH=none
Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Change-Id: Id4f84d24325566a9f648194166bde0d94d1124dc
Reviewed-on: https://chromium-review.googlesource.com/169050
Reviewed-by: Bill Richardson <wfrichar@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Commit-Queue: Daisuke Nojiri <dnojiri@google.com>
Tested-by: Daisuke Nojiri <dnojiri@google.com>
This commit is contained in:
Daisuke Nojiri
2013-12-11 11:39:55 -08:00
committed by chrome-internal-fetch
parent 700adf4dea
commit 3ec36e0160
3 changed files with 130 additions and 70 deletions

View File

@@ -225,32 +225,53 @@ void system_disable_jump(void)
disable_jump = 1;
#ifdef CONFIG_MPU
/*
* Lock down memory to prevent code execution from data areas.
*
* TODO(crosbug.com/p/16904): Also lock down the image which isn't
* running (RO if RW, or vice versa), so a bad or malicious jump can't
* execute code from that image.
*/
if (system_is_locked()) {
int ret;
int enable_mpu = 0;
enum system_image_copy_t copy;
CPRINTF("[%T MPU type: %08x]\n", mpu_get_type());
/*
* Protect memory from code execution
* Protect RAM from code execution
*/
int mpu_error = mpu_protect_ram();
if (mpu_error == EC_SUCCESS) {
mpu_enable();
ret = mpu_protect_ram();
if (ret == EC_SUCCESS) {
enable_mpu = 1;
CPRINTF("[%T RAM locked. Exclusion %08x-%08x]\n",
&__iram_text_start, &__iram_text_end);
} else {
CPRINTF("[%T Failed to lock RAM (%d). mpu_type:%08x]\n",
mpu_error, mpu_get_type());
CPRINTF("[%T Failed to lock RAM (%d)]\n", ret);
}
/*
* Protect the other image from code execution
* TODO: https://chromium-review.googlesource.com/#/c/169050/
* Protect inactive image (ie. RO if running RW, vice versa)
* from code execution.
*/
switch (system_get_image_copy()) {
case SYSTEM_IMAGE_RO:
ret = mpu_lock_rw_flash();
copy = SYSTEM_IMAGE_RW;
break;
case SYSTEM_IMAGE_RW:
ret = mpu_lock_ro_flash();
copy = SYSTEM_IMAGE_RO;
break;
default:
copy = SYSTEM_IMAGE_UNKNOWN;
ret = !EC_SUCCESS;
}
if (ret == EC_SUCCESS) {
enable_mpu = 1;
CPRINTF("[%T %s image locked]\n", image_names[copy]);
} else {
CPRINTF("[%T Failed to lock %s image (%d)]\n",
image_names[copy], ret);
}
if (enable_mpu)
mpu_enable();
} else {
CPRINTF("[%T RAM not locked]\n");
CPRINTF("[%T System is unlocked. Skip MPU configuration\n");
}
#endif
}

View File

@@ -20,39 +20,27 @@
#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.
/*
* XN (execute never) bit. It's bit 12 if accessed by halfword.
* 0: XN off
* 1: XN on
*/
int mpu_config_region(uint8_t region, uint32_t addr, uint32_t size,
uint16_t attr, uint8_t enable);
#define MPU_ATTR_XN (1 << 12)
/* AP bit. See table 3-5 of Stellaris LM4F232H5QC datasheet for details */
#define MPU_ATTR_NO_NO (0 << 8) /* previleged no access, unprev no access */
#define MPU_ATTR_RW_RW (3 << 8) /* previleged ReadWrite, unprev ReadWrite */
#define MPU_ATTR_RO_NO (5 << 8) /* previleged Read-only, unprev no access */
/* Suggested value for TEX S/C/B bit. See table 3-6 of Stellaris LM4F232H5QC
* datasheet and table 38 of STM32F10xxx Cortex-M3 programming manual. */
#define MPU_ATTR_INTERNAL_SRAM 6 /* for Internal SRAM */
#define MPU_ATTR_FLASH_MEMORY 2 /* for flash memory */
/**
* Set a region non-executable.
*
* region: number of the region
* addr: base address of the region
* size: size of the region in bytes
* Enable MPU
*/
int mpu_nx_region(uint8_t region, uint32_t addr, uint32_t size);
/**
* Enable MPU */
void mpu_enable(void);
/**
@@ -70,10 +58,16 @@ extern char __iram_text_start;
extern char __iram_text_end;
/**
* Lock down RAM
* Protect RAM from code execution
*/
int mpu_protect_ram(void);
/**
* Protect flash memory from code execution
*/
int mpu_lock_ro_flash(void);
int mpu_lock_rw_flash(void);
/**
* Initialize MPU.
* It disables all regions if MPU is implemented. Otherwise, returns

View File

@@ -11,20 +11,28 @@
#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.
*
* region: number of the region to update
* 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 of the region. Current value will be overwritten if enable
* is set.
* attr: attribute bits. Current value will be overwritten if enable is true.
* 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)
uint16_t attr, uint8_t enable)
{
asm volatile("isb; dsb;");
@@ -39,8 +47,19 @@ static void mpu_update_region(uint8_t region, uint32_t addr, uint8_t size_bit,
asm volatile("isb; dsb;");
}
int mpu_config_region(uint8_t region, uint32_t addr, uint32_t size,
uint16_t attr, uint8_t enable)
/**
* 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 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;
@@ -60,11 +79,34 @@ int mpu_config_region(uint8_t region, uint32_t addr, uint32_t size,
return EC_SUCCESS;
}
int mpu_nx_region(uint8_t region, uint32_t addr, uint32_t size)
/**
* 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_NX | MPU_ATTR_FULLACCESS | MPU_ATTR_INTERNALSRAM, 1);
return mpu_config_region(region, addr, size,
MPU_ATTR_XN | MPU_ATTR_RW_RW | texscb, 1);
}
/**
* 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)
@@ -82,27 +124,32 @@ 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);
ret = mpu_lock_region(REGION_IRAM, CONFIG_RAM_BASE,
CONFIG_RAM_SIZE, MPU_ATTR_INTERNAL_SRAM);
if (ret != EC_SUCCESS)
return ret;
ret = mpu_config_region(
7, (uint32_t)&__iram_text_start,
ret = mpu_unlock_region(
REGION_IRAM_TEXT, (uint32_t)&__iram_text_start,
(uint32_t)(&__iram_text_end - &__iram_text_start),
MPU_ATTR_FULLACCESS | MPU_ATTR_INTERNALSRAM, 1);
MPU_ATTR_INTERNAL_SRAM);
return ret;
}
int mpu_lock_ro_flash(void)
{
return mpu_lock_region(REGION_FLASH_MEMORY, CONFIG_FW_RO_OFF,
CONFIG_FW_IMAGE_SIZE, MPU_ATTR_FLASH_MEMORY);
}
int mpu_lock_rw_flash(void)
{
return mpu_lock_region(REGION_FLASH_MEMORY, CONFIG_FW_RW_OFF,
CONFIG_FW_IMAGE_SIZE, MPU_ATTR_FLASH_MEMORY);
}
int mpu_pre_init(void)
{
int i;
@@ -112,9 +159,7 @@ int mpu_pre_init(void)
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);
mpu_config_region(i, CONFIG_RAM_BASE, CONFIG_RAM_SIZE, 0, 0);
}
return EC_SUCCESS;