Files
OpenCellular/common/fmap.c
Nicolas Boichat ca0e826859 common/rollback: Add support for rollback protection
Implement actual rollback protection. First, we add a new field
in the version structure, which is an incrementing integer
(we'll start by shipping images with version 0, and gradually
increase the number as required). This allows us to release
new versions of the EC without necessarily bumping the rollback
protection.

For the rollback protection block itself, it contains 2 sub-blocks
of equal size (normally, 2k), that are individually erasable.
The rollback code looks at both, and takes the most restrictive one
to determine the desired rollback minimum version. The blocks
are also allowed to be erased (full of 1's), in which case the
rollback minimum version is assumed to be 0.

We also add an FMAP entry, in case we later decide to allow the
signer to increment the rollback version.

Also note that, like any version_data struct change, this change
breaks compatibility between old and new RO/RW.

Follow-up code will take care of auto-updating the rollback block
as required, and properly manage block protection.

BRANCH=none
BUG=b:35586219
TEST=Flash hammer
     rollbackinfo => 1 version 0 block, 1 empty block, RW verifies
           correctly.
     rollbackupdate 0; rollbackinfo => No change
     rollbackupdate 1; reboot => RO refuses to jump to RW
     rollbackupdate 2, 3, 4; rollbackinfo => Writes alternate
           between the 2 blocks.
     rollbackupdate 2 => Refuses to downgrade version

Change-Id: Ia969afb481a93deb912b9153bdd95ace01ad8fa7
Reviewed-on: https://chromium-review.googlesource.com/452815
Commit-Ready: Nicolas Boichat <drinkcat@chromium.org>
Tested-by: Nicolas Boichat <drinkcat@chromium.org>
Reviewed-by: Randall Spangler <rspangler@chromium.org>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
2017-04-06 03:29:38 -07:00

222 lines
6.6 KiB
C

/*
* Copyright (c) 2012 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.
*/
#include <stddef.h>
#include "common.h"
#include "rsa.h"
#include "util.h"
#include "version.h"
/* FMAP structs. See http://code.google.com/p/flashmap/wiki/FmapSpec */
#define FMAP_NAMELEN 32
#define FMAP_SIGNATURE "__FMAP__"
#define FMAP_SIGNATURE_SIZE 8
#define FMAP_VER_MAJOR 1
#define FMAP_VER_MINOR 0
/*
* For address containing CONFIG_PROGRAM_MEMORY_BASE (symbols in *.RO.lds.S and
* variable), this computes the offset to the start of the image on flash.
*/
#define RELATIVE_RO(addr) ((addr) - CONFIG_PROGRAM_MEMORY_BASE - \
CONFIG_RO_MEM_OFF)
/*
* All internal EC code assumes that offsets are provided relative to
* physical address zero of storage. In some cases, the region of storage
* belonging to the EC is not physical address zero - a non-zero fmap_base
* indicates so. Since fmap_base is not yet handled correctly by external
* code, we must perform the adjustment in our host command handlers -
* adjust all offsets so they are relative to the beginning of the storage
* region belonging to the EC. TODO(crbug.com/529365): Handle fmap_base
* correctly in flashrom, dump_fmap, etc. and remove EC_FLASH_REGION_START.
*/
#if CONFIG_EC_WRITABLE_STORAGE_OFF < CONFIG_EC_PROTECTED_STORAGE_OFF
#define FMAP_REGION_START CONFIG_EC_WRITABLE_STORAGE_OFF
#else
#define FMAP_REGION_START CONFIG_EC_PROTECTED_STORAGE_OFF
#endif
struct fmap_header {
char fmap_signature[FMAP_SIGNATURE_SIZE];
uint8_t fmap_ver_major;
uint8_t fmap_ver_minor;
uint64_t fmap_base;
uint32_t fmap_size;
char fmap_name[FMAP_NAMELEN];
uint16_t fmap_nareas;
} __packed;
#define FMAP_AREA_STATIC (1 << 0) /* can be checksummed */
#define FMAP_AREA_COMPRESSED (1 << 1) /* may be compressed */
#define FMAP_AREA_RO (1 << 2) /* writes may fail */
struct fmap_area_header {
uint32_t area_offset;
uint32_t area_size;
char area_name[FMAP_NAMELEN];
uint16_t area_flags;
} __packed;
#ifdef CONFIG_RWSIG_TYPE_RWSIG
#define NUM_EC_FMAP_AREAS_RWSIG 2
#else
#define NUM_EC_FMAP_AREAS_RWSIG 0
#endif
#ifdef CONFIG_ROLLBACK
#define NUM_EC_FMAP_AREAS_ROLLBACK 1
#else
#define NUM_EC_FMAP_AREAS_ROLLBACK 0
#endif
#define NUM_EC_FMAP_AREAS (7 + \
NUM_EC_FMAP_AREAS_RWSIG + \
NUM_EC_FMAP_AREAS_ROLLBACK)
const struct _ec_fmap {
struct fmap_header header;
struct fmap_area_header area[NUM_EC_FMAP_AREAS];
} ec_fmap __keep __attribute__((section(".google"))) = {
/* Header */
{
.fmap_signature = {'_', '_', 'F', 'M', 'A', 'P', '_', '_'},
.fmap_ver_major = FMAP_VER_MAJOR,
.fmap_ver_minor = FMAP_VER_MINOR,
.fmap_base = CONFIG_PROGRAM_MEMORY_BASE,
.fmap_size = CONFIG_FLASH_SIZE,
/* Used to distinguish the EC FMAP from other FMAPs */
.fmap_name = "EC_FMAP",
.fmap_nareas = NUM_EC_FMAP_AREAS,
},
{
/* RO Firmware */
{
/*
* Range of RO firmware to be updated. Verified in
* factory finalization by hash. Should not have
* volatile data (ex, calibration results).
*/
.area_name = "EC_RO",
.area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF -
FMAP_REGION_START + CONFIG_RO_STORAGE_OFF,
.area_size = CONFIG_RO_SIZE,
.area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO,
},
{
/* (Optional) RO firmware code. */
.area_name = "FR_MAIN",
.area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF -
FMAP_REGION_START + CONFIG_RO_STORAGE_OFF,
.area_size = CONFIG_RO_SIZE,
.area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO,
},
{
/*
* RO firmware version ID. Must be NULL terminated
* ASCII, and padded with \0.
*/
.area_name = "RO_FRID",
.area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF -
FMAP_REGION_START + CONFIG_RO_STORAGE_OFF +
RELATIVE_RO((uint32_t)__image_data_offset) +
offsetof(struct image_data, version),
.area_size = sizeof(current_image_data.version),
.area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO,
},
/* Other RO stuff: FMAP, WP, KEYS, etc. */
{
.area_name = "FMAP",
.area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF -
FMAP_REGION_START + CONFIG_RO_STORAGE_OFF +
RELATIVE_RO((uint32_t)&ec_fmap),
.area_size = sizeof(ec_fmap),
.area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO,
},
{
/*
* The range for write protection, for factory
* finalization. Should include (may be identical to)
* EC_RO and aligned to hardware specification.
*/
.area_name = "WP_RO",
.area_offset = CONFIG_WP_STORAGE_OFF -
FMAP_REGION_START,
.area_size = CONFIG_WP_STORAGE_SIZE,
.area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO,
},
#ifdef CONFIG_RWSIG_TYPE_RWSIG
{
/* RO public key address, for RW verification */
.area_name = "KEY_RO",
.area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF -
FMAP_REGION_START + CONFIG_RO_PUBKEY_ADDR -
CONFIG_PROGRAM_MEMORY_BASE,
.area_size = CONFIG_RO_PUBKEY_SIZE,
.area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO,
},
#endif
/* RW Firmware */
{
/* The range of RW firmware to be auto-updated. */
.area_name = "EC_RW",
.area_offset = CONFIG_EC_WRITABLE_STORAGE_OFF -
FMAP_REGION_START + CONFIG_RW_STORAGE_OFF,
.area_size = CONFIG_RW_SIZE,
.area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO,
},
{
/*
* RW firmware version ID. Must be NULL terminated
* ASCII, and padded with \0.
* TODO: Get the relative offset of
* __image_data_offset within our RW image to
* accommodate image asymmetry.
*/
.area_name = "RW_FWID",
.area_offset = CONFIG_EC_WRITABLE_STORAGE_OFF -
FMAP_REGION_START + CONFIG_RW_STORAGE_OFF +
RELATIVE_RO((uint32_t)__image_data_offset) +
offsetof(struct image_data, version),
.area_size = sizeof(current_image_data.version),
.area_flags = FMAP_AREA_STATIC,
},
#ifdef CONFIG_ROLLBACK
{
/*
* RW rollback version, 32-bit unsigned integer.
* TODO: Get the relative offset of
* __image_data_offset within our RW image to
* accommodate image asymmetry.
*/
.area_name = "RW_RBVER",
.area_offset = CONFIG_EC_WRITABLE_STORAGE_OFF -
FMAP_REGION_START + CONFIG_RW_STORAGE_OFF +
RELATIVE_RO((uint32_t)__image_data_offset) +
offsetof(struct image_data, rollback_version),
.area_size = sizeof(
current_image_data.rollback_version),
.area_flags = FMAP_AREA_STATIC,
},
#endif
#ifdef CONFIG_RWSIG_TYPE_RWSIG
{
/* RW image signature */
.area_name = "SIG_RW",
.area_offset = CONFIG_EC_PROTECTED_STORAGE_OFF -
FMAP_REGION_START + CONFIG_RW_SIG_ADDR -
CONFIG_PROGRAM_MEMORY_BASE,
.area_size = CONFIG_RW_SIG_SIZE,
.area_flags = FMAP_AREA_STATIC | FMAP_AREA_RO,
},
#endif
}
};