mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2025-12-31 02:51:26 +00:00
This is useful for debugging, to understand if the RW rollback version is ahead of current rollback version. BRANCH=none BUG=b:35586219 TEST=Flash hammer, rollbackinfo in EC console Change-Id: I2634199845f1b35447e0938a35b862f79cb24ffa Reviewed-on: https://chromium-review.googlesource.com/489886 Commit-Ready: Nicolas Boichat <drinkcat@chromium.org> Tested-by: Nicolas Boichat <drinkcat@chromium.org> Reviewed-by: Randall Spangler <rspangler@chromium.org>
218 lines
5.2 KiB
C
218 lines
5.2 KiB
C
/* Copyright 2017 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.
|
|
*/
|
|
|
|
/* Rollback protection logic. */
|
|
|
|
#include "common.h"
|
|
#include "console.h"
|
|
#include "flash.h"
|
|
#include "rollback.h"
|
|
#include "system.h"
|
|
#include "util.h"
|
|
|
|
/* Console output macros */
|
|
#define CPRINTS(format, args...) cprints(CC_SYSTEM, format, ## args)
|
|
|
|
/* Number of rollback regions */
|
|
#define ROLLBACK_REGIONS 2
|
|
|
|
/*
|
|
* Note: Do not change this structure without also updating
|
|
* common/firmware_image.S .image.ROLLBACK section.
|
|
*/
|
|
struct rollback_data {
|
|
int32_t rollback_min_version;
|
|
uint32_t cookie;
|
|
};
|
|
|
|
/* We need at least 2 erasable blocks in the rollback region. */
|
|
BUILD_ASSERT(CONFIG_ROLLBACK_SIZE >= ROLLBACK_REGIONS*CONFIG_FLASH_ERASE_SIZE);
|
|
BUILD_ASSERT(sizeof(struct rollback_data) <= CONFIG_FLASH_ERASE_SIZE);
|
|
|
|
static uintptr_t get_rollback_offset(int region)
|
|
{
|
|
return CONFIG_ROLLBACK_OFF + region * CONFIG_FLASH_ERASE_SIZE;
|
|
}
|
|
|
|
static int read_rollback(int region, struct rollback_data *data)
|
|
{
|
|
uintptr_t offset;
|
|
|
|
offset = get_rollback_offset(region);
|
|
|
|
if (flash_read(offset, sizeof(*data), (char *)data))
|
|
return EC_ERROR_UNKNOWN;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* Get the most recent rollback information.
|
|
*
|
|
* @rollback_min_version: Minimum version to accept for rollback protection,
|
|
* or 0 if no rollback information is present.
|
|
*
|
|
* Return most recent region index on success (>= 0, or 0 if no rollback
|
|
* region is valid), negative value on error.
|
|
*/
|
|
static int get_latest_rollback(int32_t *rollback_min_version)
|
|
{
|
|
int region;
|
|
int min_region = 0;
|
|
|
|
*rollback_min_version = 0;
|
|
|
|
for (region = 0; region < ROLLBACK_REGIONS; region++) {
|
|
struct rollback_data data;
|
|
|
|
if (read_rollback(region, &data))
|
|
return -1;
|
|
|
|
/* Check if not initialized or invalid cookie. */
|
|
if (data.cookie != CROS_EC_ROLLBACK_COOKIE)
|
|
continue;
|
|
|
|
if (data.rollback_min_version > *rollback_min_version) {
|
|
min_region = region;
|
|
*rollback_min_version = data.rollback_min_version;
|
|
}
|
|
}
|
|
|
|
return min_region;
|
|
}
|
|
|
|
int32_t rollback_get_minimum_version(void)
|
|
{
|
|
int32_t rollback_min_version;
|
|
|
|
if (get_latest_rollback(&rollback_min_version) < 0)
|
|
return -1;
|
|
|
|
return rollback_min_version;
|
|
}
|
|
|
|
int rollback_lock(void)
|
|
{
|
|
int ret;
|
|
|
|
/* Already locked */
|
|
if (flash_get_protect() & EC_FLASH_PROTECT_ROLLBACK_NOW)
|
|
return EC_SUCCESS;
|
|
|
|
CPRINTS("Protecting rollback");
|
|
|
|
/* This may do nothing if WP is not enabled, or RO is not protected. */
|
|
ret = flash_set_protect(EC_FLASH_PROTECT_ROLLBACK_AT_BOOT, -1);
|
|
|
|
if (!(flash_get_protect() & EC_FLASH_PROTECT_ROLLBACK_NOW) &&
|
|
flash_get_protect() & EC_FLASH_PROTECT_ROLLBACK_AT_BOOT) {
|
|
/*
|
|
* If flash protection is still not enabled (some chips may
|
|
* be able to enable it immediately), reboot.
|
|
*/
|
|
cflush();
|
|
system_reset(SYSTEM_RESET_HARD | SYSTEM_RESET_PRESERVE_FLAGS);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int rollback_update(int32_t next_min_version)
|
|
{
|
|
struct rollback_data data;
|
|
uintptr_t offset;
|
|
int region;
|
|
int32_t current_min_version;
|
|
|
|
if (flash_get_protect() & EC_FLASH_PROTECT_ROLLBACK_NOW)
|
|
return EC_ERROR_ACCESS_DENIED;
|
|
|
|
region = get_latest_rollback(¤t_min_version);
|
|
|
|
if (region < 0)
|
|
return EC_ERROR_UNKNOWN;
|
|
|
|
/* Do not accept to decrement the value. */
|
|
if (next_min_version < current_min_version)
|
|
return EC_ERROR_INVAL;
|
|
|
|
/* No need to update if version is already correct. */
|
|
if (next_min_version == current_min_version)
|
|
return EC_SUCCESS;
|
|
|
|
/* Use the other region. */
|
|
region = (region + 1) % ROLLBACK_REGIONS;
|
|
|
|
offset = get_rollback_offset(region);
|
|
|
|
data.rollback_min_version = next_min_version;
|
|
data.cookie = CROS_EC_ROLLBACK_COOKIE;
|
|
|
|
/* Offset should never be part of active image. */
|
|
if (system_unsafe_to_overwrite(offset, CONFIG_FLASH_ERASE_SIZE))
|
|
return EC_ERROR_UNKNOWN;
|
|
|
|
if (flash_erase(offset, CONFIG_FLASH_ERASE_SIZE))
|
|
return EC_ERROR_UNKNOWN;
|
|
|
|
if (flash_write(offset, sizeof(data), (char *)&data))
|
|
return EC_ERROR_UNKNOWN;
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
|
|
static int command_rollback_info(int argc, char **argv)
|
|
{
|
|
int region, ret, min_region;
|
|
int32_t rollback_min_version;
|
|
int32_t rw_rollback_version;
|
|
|
|
min_region = get_latest_rollback(&rollback_min_version);
|
|
|
|
if (min_region < 0)
|
|
return EC_ERROR_UNKNOWN;
|
|
|
|
rw_rollback_version = system_get_rollback_version(SYSTEM_IMAGE_RW);
|
|
|
|
ccprintf("rollback minimum version: %d\n", rollback_min_version);
|
|
ccprintf("RW rollback version: %d\n", rw_rollback_version);
|
|
|
|
for (region = 0; region < ROLLBACK_REGIONS; region++) {
|
|
struct rollback_data data;
|
|
|
|
ret = read_rollback(region, &data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ccprintf("rollback %d: %08x %08x%s\n",
|
|
region, data.rollback_min_version, data.cookie,
|
|
min_region == region ? " *" : "");
|
|
}
|
|
|
|
return EC_SUCCESS;
|
|
}
|
|
DECLARE_SAFE_CONSOLE_COMMAND(rollbackinfo, command_rollback_info,
|
|
NULL,
|
|
"Print rollback info");
|
|
|
|
static int command_rollback_update(int argc, char **argv)
|
|
{
|
|
int32_t min_version;
|
|
char *e;
|
|
|
|
if (argc < 2)
|
|
return EC_ERROR_PARAM_COUNT;
|
|
|
|
min_version = strtoi(argv[1], &e, 0);
|
|
|
|
if (*e || min_version < 0)
|
|
return EC_ERROR_PARAM1;
|
|
|
|
return rollback_update(min_version);
|
|
}
|
|
DECLARE_CONSOLE_COMMAND(rollbackupdate, command_rollback_update,
|
|
"min_version",
|
|
"Update rollback info");
|