g: mark RW INFO rollback map space to match the header infomap field

The cr50 RO image compares INFO rollback map space against the
contents of the RW image's infomap field.

To ensure that no rollback is possible, the RW should verify that the
INFO space state is consistent with the current RW and RW_B headers,
and if not, update the INFO state to comply.

This should happen only when running prod images, so that debug images
could be rolled back if so desired.

Also fixed the bug in functions enabling read and write access to the
INFO1 region. Write access is now a superset of read access setting.

BRANCH=cr50
BUG=b:35774863
TEST=as follows:
  - built and ran a new image, observed it start successfully;
  - modified the manifest to erase the first map location, built and
    ran a new image, observed it start successfully
  - restored the manifest, built and tried running a new image,
    observed that the earlier version is starting.

Change-Id: I62253c3e98cd24ed24424b8bb9de22692a262d89
Signed-off-by: Vadim Bendebury <vbendeb@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/447966
Reviewed-by: Marius Schilder <mschilder@chromium.org>
Reviewed-by: Mary Ruthven <mruthven@chromium.org>
This commit is contained in:
Vadim Bendebury
2017-02-28 18:16:42 -08:00
committed by chrome-bot
parent b1e212dabe
commit 46d6b04712
5 changed files with 142 additions and 10 deletions

View File

@@ -542,6 +542,8 @@ static void board_init(void)
/* Initialize the persistent storage. */
initvars();
system_update_rollback_mask();
/* Indication that firmware is running, for debug purposes. */
GREG32(PMU, PWRDN_SCRATCH16) = 0xCAFECAFE;

View File

@@ -371,20 +371,20 @@ static int valid_info_range(uint32_t offset, size_t size)
}
/* Write access is a superset of read access. */
static int flash_info_configure_access(uint32_t offset,
size_t size, int read_mode)
size_t size, int write_mode)
{
int mask;
if (!valid_info_range(offset, size))
return EC_ERROR_INVAL;
if (read_mode)
mask = GC_GLOBALSEC_FLASH_REGION6_CTRL_EN_MASK |
GC_GLOBALSEC_FLASH_REGION6_CTRL_RD_EN_MASK;
else
mask = GC_GLOBALSEC_FLASH_REGION6_CTRL_EN_MASK |
GC_GLOBALSEC_FLASH_REGION6_CTRL_WR_EN_MASK;
mask = GREG32(GLOBALSEC, FLASH_REGION6_CTRL);
mask |= GC_GLOBALSEC_FLASH_REGION6_CTRL_EN_MASK |
GC_GLOBALSEC_FLASH_REGION6_CTRL_RD_EN_MASK;
if (write_mode)
mask |= GC_GLOBALSEC_FLASH_REGION6_CTRL_WR_EN_MASK;
GREG32(GLOBALSEC, FLASH_REGION6_BASE_ADDR) =
FLASH_INFO_MEMORY_BASE + offset;
@@ -397,12 +397,12 @@ static int flash_info_configure_access(uint32_t offset,
int flash_info_read_enable(uint32_t offset, size_t size)
{
return flash_info_configure_access(offset, size, 1);
return flash_info_configure_access(offset, size, 0);
}
int flash_info_write_enable(uint32_t offset, size_t size)
{
return flash_info_configure_access(offset, size, 0);
return flash_info_configure_access(offset, size, 1);
}
void flash_info_write_disable(void)

View File

@@ -8,7 +8,23 @@
#include <stddef.h>
#include "signed_header.h"
/*
* Info1 space available to the app firmware is split in several areas. Of
* interest are the two spaces used for rollback prevention of RO and RW image
* versions.
*
* Each bit in the image infomap header section is mapped into a 4 byte word
* in the Info1 space.
*/
#define INFO_RO_MAP_OFFSET 0
#define INFO_RO_MAP_SIZE (INFO_MAX * 4)
#define INFO_RW_MAP_OFFSET INFO_RO_MAP_SIZE
#define INFO_RW_MAP_SIZE (INFO_MAX * 4)
int flash_info_read_enable(uint32_t offset, size_t size);
/* This in fact enables both read and write. */
int flash_info_write_enable(uint32_t offset, size_t size);
void flash_info_write_disable(void);
int flash_info_physical_write(int byte_offset, int num_bytes, const char *data);

View File

@@ -8,9 +8,9 @@
#include "cpu.h"
#include "cpu.h"
#include "flash.h"
#include "flash_info.h"
#include "printf.h"
#include "registers.h"
#include "signed_header.h"
#include "system.h"
#include "system_chip.h"
#include "task.h"
@@ -513,3 +513,111 @@ const char *system_get_build_info(void)
return combined_build_info;
}
void system_update_rollback_mask(void)
{
#ifndef CR50_DEV
int updated_words_count = 0;
int i;
int write_enabled = 0;
uint32_t header_mask = 0;
const struct SignedHeader *header_a;
const struct SignedHeader *header_b;
header_a = (const struct SignedHeader *)
get_program_memory_addr(SYSTEM_IMAGE_RW);
header_b = (const struct SignedHeader *)
get_program_memory_addr(SYSTEM_IMAGE_RW_B);
/*
* Make sure INFO1 RW map space is readable.
*/
if (flash_info_read_enable(INFO_RW_MAP_OFFSET, INFO_RW_MAP_SIZE) !=
EC_SUCCESS) {
ccprintf("%s: failed to enable read access to info\n",
__func__);
return;
}
/*
* The infomap field in the image header has a matching space in the
* flash INFO1 section.
*
* The INFO1 space words which map into zeroed bits in the infomap
* header are ignored by the RO.
*
* Let's make sure that those words in the INFO1 space are erased.
* This in turn makes sure that attempts to load earlier RW images
* (where those bits in the header are not zeroed) will fail, thus
* ensuring rollback protection.
*/
/* For each bit in the header infomap field of the running image. */
for (i = 0; i < INFO_MAX; i++) {
uint32_t bit;
uint32_t word;
int byte_offset;
/* Read the next infomap word when done with the current one. */
if (!(i % 32)) {
/*
* Not to shoot ourselves in the foot, let's zero only
* those words in the INFO1 space which have both A
* and B header's infomap bit set to zero.
*/
header_mask =
header_a->infomap[i/32] |
header_b->infomap[i/32];
}
/* Get the next bit value. */
bit = !!(header_mask & (1 << (i % 32)));
if (bit) {
/*
* By convention zeroed bits are expected to be
* adjacent at the LSB of the info mask field. Stop as
* soon as a non-zeroed bit is encountered.
*/
ccprintf("%s: bailing out at bit %d\n", __func__, i);
break;
}
byte_offset = (INFO_MAX + i) * sizeof(uint32_t);
if (flash_physical_info_read_word(byte_offset, &word) !=
EC_SUCCESS) {
ccprintf("failed to read info mask word %d\n", i);
continue;
}
if (!word)
continue; /* This word has been zeroed already. */
if (!write_enabled) {
if (flash_info_write_enable(
INFO_RW_MAP_OFFSET,
INFO_RW_MAP_SIZE) != EC_SUCCESS) {
ccprintf("%s: failed to enable write access to"
" info\n", __func__);
return;
}
write_enabled = 1;
}
word = 0;
if (flash_info_physical_write(byte_offset,
sizeof(word),
(const char *) &word) !=
EC_SUCCESS) {
ccprintf("failed to write info mask word %d\n", i);
continue;
}
updated_words_count++;
}
if (!write_enabled)
return;
flash_info_write_disable();
ccprintf("updated %d info map words\n", updated_words_count);
#endif /* CR50_DEV ^^^^^^^^ NOT defined. */
}

View File

@@ -51,4 +51,10 @@ int system_rollback_detected(void);
*/
int system_battery_cutoff_support_required(void);
/**
* Modify info1 RW rollback mask to match currently executing RW image's
* header.
*/
void system_update_rollback_mask(void);
#endif /* __CROS_EC_SYSTEM_CHIP_H */