mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-04 14:01:54 +00:00
cr50: Add SPI hashing command
This allows hashing or dumping SPI flash from the Cr50 console even on a locked device, so you can verify the RO Firmware on a system via CCD. See design doc: go/verify-ro-firmware (more specifically, "Cr50 console commands for option 1") BUG=chromium:804507 BRANCH=cr50 release (after testing) TEST=manual: # Sample sequence spihash ap -> requires physical presence; tap power button spihash 0 1024 -> gives a hash; compare with first 1KB of image.bin spihash 0 128 dump -> dumps first 128 bytes; compare with image.bin spihash 128 128 -> offset works spihash 0 0x100000 -> gives a hash; doesn't watchdog reset spihdev ec spihash 0 1024 -> compare with ec.bin spihash disable # Test timeout spihash ap # Wait 30 seconds spihash 0 1024 -> still works # Wait 60 seconds; goes back disabled automatically spihash 0 1024 -> fails because spihash is disabled # Presence not required when CCD opened ccd open spihash ap -> no PP required spihash 0 1024 -> works spihash disable # Possible for owner to disable via CCD config ccd -> HashFlash is "Always" ccd set HashFlash IfOpened ccd lock spihash ap -> access denied # Cleanup ccd open ccd reset ccd lock Change-Id: I27b5054730dea6b27fbad1b1c4aa0a650e3b4f99 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/889725 Reviewed-by: Vadim Bendebury <vbendeb@chromium.org>
This commit is contained in:
committed by
chrome-bot
parent
85caeb6ccb
commit
ff4d22819a
@@ -4,17 +4,134 @@
|
||||
*/
|
||||
|
||||
#include "ccd_config.h"
|
||||
#include "cryptoc/sha256.h"
|
||||
#include "console.h"
|
||||
#include "dcrypto.h"
|
||||
#include "gpio.h"
|
||||
#include "hooks.h"
|
||||
#include "physical_presence.h"
|
||||
#include "registers.h"
|
||||
#include "spi.h"
|
||||
#include "spi_flash.h"
|
||||
#include "system.h"
|
||||
#include "task.h"
|
||||
#include "timer.h"
|
||||
#include "usb_spi.h"
|
||||
|
||||
#define CPRINTS(format, args...) cprints(CC_USB, format, ## args)
|
||||
|
||||
/* Don't hash more than this at once */
|
||||
#define MAX_SPI_HASH_SIZE (4 * 1024 * 1024)
|
||||
|
||||
/*
|
||||
* Buffer size to use for reading and hashing. This must be a multiple of the
|
||||
* SHA256 block size (64 bytes) and at least 4 less than the maximum SPI
|
||||
* transaction size for H1 (0x80 bytes). So, 64.
|
||||
*/
|
||||
#define SPI_HASH_CHUNK_SIZE 64
|
||||
|
||||
/* Timeout for auto-disabling SPI hash device, in microseconds */
|
||||
#define SPI_HASH_TIMEOUT_US (60 * SECOND)
|
||||
|
||||
/* Current device for SPI hashing */
|
||||
static uint8_t spi_hash_device = USB_SPI_DISABLE;
|
||||
|
||||
/*
|
||||
* Do we need to use NPCX7 gang programming mode?
|
||||
*
|
||||
* If 0, then we hold the EC in reset the whole time we've acquired the SPI
|
||||
* bus, to keep the EC from accessing it.
|
||||
*
|
||||
* If 1, then:
|
||||
*
|
||||
* When we acquire the EC SPI bus, we need to reset the EC, assert the
|
||||
* gang programmer enable, then take the EC out of reset so its boot ROM
|
||||
* can map the EC's internal SPI bus to the EC gang programmer pins.
|
||||
*
|
||||
* When we relinquish the EC SPI bus, we need to reset the EC again while
|
||||
* keeping gang programmer deasserted, then take the EC out of reset. The
|
||||
* EC will then boot normally.
|
||||
*/
|
||||
static uint8_t use_npcx_gang_mode;
|
||||
|
||||
/*
|
||||
* Device and gang mode selected by last spihash command, for use by
|
||||
* spi_hash_pp_done().
|
||||
*/
|
||||
static uint8_t new_device;
|
||||
static uint8_t new_gang_mode;
|
||||
|
||||
static void spi_hash_inactive_timeout(void);
|
||||
DECLARE_DEFERRED(spi_hash_inactive_timeout);
|
||||
|
||||
/*****************************************************************************/
|
||||
/*
|
||||
* Mutex and variable for tracking whether the SPI bus is used by the USB
|
||||
* connection or hashing commands.
|
||||
*
|
||||
* Access these ONLY through set_spi_bus_user() and get_spi_bus_user(), to
|
||||
* ensure thread-safe access to the SPI bus.
|
||||
*/
|
||||
static struct mutex spi_bus_user_mutex;
|
||||
static enum spi_bus_user_t {
|
||||
SPI_BUS_USER_NONE = 0,
|
||||
SPI_BUS_USER_USB,
|
||||
SPI_BUS_USER_HASH
|
||||
} spi_bus_user = SPI_BUS_USER_NONE;
|
||||
|
||||
/**
|
||||
* Set who's using the SPI bus.
|
||||
*
|
||||
* This is thread-safe and will not block if someone owns the bus. You can't
|
||||
* take the bus if someone else has it, and you can only free it if you hold
|
||||
* it. It has no extra effect if you already own the bus.
|
||||
*
|
||||
* @param user What bus user is asking?
|
||||
* @param want_bus Do we want the bus (!=0) or no longer want it (==0)?
|
||||
*
|
||||
* @return EC_SUCCESS, or non-zero error code.
|
||||
*/
|
||||
static int set_spi_bus_user(enum spi_bus_user_t user, int want_bus)
|
||||
{
|
||||
int rv = EC_SUCCESS;
|
||||
|
||||
/*
|
||||
* Serialize access to bus user variable, but don't mutex lock the
|
||||
* entire bus because that would freeze USB or the console instead of
|
||||
* just failing.
|
||||
*/
|
||||
mutex_lock(&spi_bus_user_mutex);
|
||||
|
||||
if (want_bus) {
|
||||
/* Can only take the bus if it's free or we already own it */
|
||||
if (spi_bus_user == SPI_BUS_USER_NONE)
|
||||
spi_bus_user = user;
|
||||
else if (spi_bus_user != user)
|
||||
rv = EC_ERROR_BUSY;
|
||||
} else {
|
||||
/* Can only free the bus if it was ours */
|
||||
if (spi_bus_user == user)
|
||||
spi_bus_user = SPI_BUS_USER_NONE;
|
||||
else
|
||||
rv = EC_ERROR_BUSY;
|
||||
}
|
||||
|
||||
mutex_unlock(&spi_bus_user_mutex);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current SPI bus user.
|
||||
*/
|
||||
static enum spi_bus_user_t get_spi_bus_user(void)
|
||||
{
|
||||
return spi_bus_user;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Methods to enable / disable the SPI bus and pin mux */
|
||||
|
||||
static void disable_ec_ap_spi(void)
|
||||
{
|
||||
int was_ap_spi_en = gpio_get_level(GPIO_AP_FLASH_SELECT);
|
||||
@@ -60,27 +177,11 @@ static void enable_ap_spi(void)
|
||||
assert_ec_rst();
|
||||
}
|
||||
|
||||
int usb_spi_board_enable(struct usb_spi_config const *config)
|
||||
/**
|
||||
* Enable the pin mux to the SPI master port.
|
||||
*/
|
||||
static void enable_spi_pinmux(void)
|
||||
{
|
||||
disable_ec_ap_spi();
|
||||
|
||||
if (config->state->enabled_host == USB_SPI_EC) {
|
||||
if (!ccd_is_cap_enabled(CCD_CAP_EC_FLASH)) {
|
||||
CPRINTS("EC SPI access denied");
|
||||
return EC_ERROR_ACCESS_DENIED;
|
||||
}
|
||||
enable_ec_spi();
|
||||
} else if (config->state->enabled_host == USB_SPI_AP) {
|
||||
if (!ccd_is_cap_enabled(CCD_CAP_AP_FLASH)) {
|
||||
CPRINTS("AP SPI access denied");
|
||||
return EC_ERROR_ACCESS_DENIED;
|
||||
}
|
||||
enable_ap_spi();
|
||||
} else {
|
||||
CPRINTS("DEVICE NOT SUPPORTED");
|
||||
return EC_ERROR_INVAL;
|
||||
}
|
||||
|
||||
GWRITE_FIELD(PINMUX, DIOA4_CTL, PD, 0); /* SPI_MOSI */
|
||||
GWRITE_FIELD(PINMUX, DIOA8_CTL, PD, 0); /* SPI_CLK */
|
||||
|
||||
@@ -95,13 +196,13 @@ int usb_spi_board_enable(struct usb_spi_config const *config)
|
||||
gpio_get_level(GPIO_AP_FLASH_SELECT) ? "AP" : "EC");
|
||||
|
||||
spi_enable(CONFIG_SPI_FLASH_PORT, 1);
|
||||
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
void usb_spi_board_disable(struct usb_spi_config const *config)
|
||||
/**
|
||||
* Disable the pin mux to the SPI master port.
|
||||
*/
|
||||
static void disable_spi_pinmux(void)
|
||||
{
|
||||
CPRINTS("usb_spi disable");
|
||||
spi_enable(CONFIG_SPI_FLASH_PORT, 0);
|
||||
|
||||
/* Disconnect SPI peripheral to tri-state pads */
|
||||
@@ -119,8 +220,62 @@ void usb_spi_board_disable(struct usb_spi_config const *config)
|
||||
GWRITE(PINMUX, DIOA4_SEL, GC_PINMUX_GPIO0_GPIO7_SEL);
|
||||
GWRITE(PINMUX, DIOA8_SEL, GC_PINMUX_GPIO0_GPIO8_SEL);
|
||||
GWRITE(PINMUX, DIOA14_SEL, GC_PINMUX_GPIO0_GPIO9_SEL);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* USB SPI methods */
|
||||
|
||||
int usb_spi_board_enable(struct usb_spi_config const *config)
|
||||
{
|
||||
int host = config->state->enabled_host;
|
||||
|
||||
/* Make sure we're allowed to enable the requested device */
|
||||
if (host == USB_SPI_EC) {
|
||||
if (!ccd_is_cap_enabled(CCD_CAP_EC_FLASH)) {
|
||||
CPRINTS("EC SPI access denied");
|
||||
return EC_ERROR_ACCESS_DENIED;
|
||||
}
|
||||
} else if (host == USB_SPI_AP) {
|
||||
if (!ccd_is_cap_enabled(CCD_CAP_AP_FLASH)) {
|
||||
CPRINTS("AP SPI access denied");
|
||||
return EC_ERROR_ACCESS_DENIED;
|
||||
}
|
||||
} else {
|
||||
CPRINTS("SPI device not supported");
|
||||
return EC_ERROR_INVAL;
|
||||
}
|
||||
|
||||
if (set_spi_bus_user(SPI_BUS_USER_USB, 1) != EC_SUCCESS) {
|
||||
CPRINTS("SPI bus in use");
|
||||
return EC_ERROR_BUSY;
|
||||
}
|
||||
|
||||
disable_ec_ap_spi();
|
||||
|
||||
/*
|
||||
* Only need to check EC vs. AP, because other hosts were ruled out
|
||||
* above.
|
||||
*/
|
||||
if (host == USB_SPI_EC)
|
||||
enable_ec_spi();
|
||||
else
|
||||
enable_ap_spi();
|
||||
|
||||
enable_spi_pinmux();
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
void usb_spi_board_disable(struct usb_spi_config const *config)
|
||||
{
|
||||
CPRINTS("usb_spi disable");
|
||||
|
||||
/* Only disable the SPI bus if we own it */
|
||||
if (get_spi_bus_user() != SPI_BUS_USER_USB)
|
||||
return;
|
||||
|
||||
disable_spi_pinmux();
|
||||
disable_ec_ap_spi();
|
||||
set_spi_bus_user(SPI_BUS_USER_USB, 0);
|
||||
}
|
||||
|
||||
int usb_spi_interface(struct usb_spi_config const *config,
|
||||
@@ -164,3 +319,307 @@ int usb_spi_interface(struct usb_spi_config const *config,
|
||||
hook_call_deferred(config->deferred, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Hashing support */
|
||||
|
||||
/**
|
||||
* Returns the content of SPI flash
|
||||
*
|
||||
* @param buf_usr Buffer to write flash contents
|
||||
* @param offset Flash offset to start reading from
|
||||
* @param bytes Number of bytes to read.
|
||||
*
|
||||
* @return EC_SUCCESS, or non-zero if any error.
|
||||
*/
|
||||
int spi_read_chunk(uint8_t *buf_usr, unsigned int offset, unsigned int bytes)
|
||||
{
|
||||
uint8_t cmd[4];
|
||||
|
||||
if (bytes > SPI_HASH_CHUNK_SIZE)
|
||||
return EC_ERROR_INVAL;
|
||||
|
||||
cmd[0] = SPI_FLASH_READ;
|
||||
cmd[1] = (offset >> 16) & 0xFF;
|
||||
cmd[2] = (offset >> 8) & 0xFF;
|
||||
cmd[3] = offset & 0xFF;
|
||||
|
||||
return spi_transaction(SPI_FLASH_DEVICE, cmd, 4, buf_usr, bytes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset EC out of gang programming mode if needed.
|
||||
*/
|
||||
static void spi_hash_stop_ec_device(void)
|
||||
{
|
||||
/* If device is not currently EC, nothing to do */
|
||||
if (spi_hash_device != USB_SPI_EC)
|
||||
return;
|
||||
|
||||
if (use_npcx_gang_mode) {
|
||||
/*
|
||||
* EC was in gang mode. Pulse reset without asserting gang
|
||||
* programmer enable, so that when we take the EC out of reset
|
||||
* it will boot normally.
|
||||
*/
|
||||
assert_ec_rst();
|
||||
usleep(200);
|
||||
use_npcx_gang_mode = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Release EC from reset (either from above, or because gang progamming
|
||||
* mode was disabled so the EC was held in reset during SPI access).
|
||||
*/
|
||||
deassert_ec_rst();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable SPI hashing mode.
|
||||
*
|
||||
* @return EC_SUCCESS or non-zero error code.
|
||||
*/
|
||||
static int spi_hash_disable(void)
|
||||
{
|
||||
/* Can't disable SPI if we don't own it */
|
||||
if (get_spi_bus_user() != SPI_BUS_USER_HASH)
|
||||
return EC_ERROR_ACCESS_DENIED;
|
||||
|
||||
/* Disable the SPI bus and chip select */
|
||||
disable_spi_pinmux();
|
||||
disable_ec_ap_spi();
|
||||
|
||||
/* Stop the EC device, if it was active */
|
||||
spi_hash_stop_ec_device();
|
||||
|
||||
/* Release the bus */
|
||||
spi_hash_device = USB_SPI_DISABLE;
|
||||
new_device = USB_SPI_DISABLE;
|
||||
new_gang_mode = 0;
|
||||
set_spi_bus_user(SPI_BUS_USER_HASH, 0);
|
||||
|
||||
/* Disable inactivity timer to turn hashing mode off */
|
||||
hook_call_deferred(&spi_hash_inactive_timeout_data, -1);
|
||||
|
||||
CPRINTS("SPI hash device: disable\n");
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deferred function to disable SPI hash mode on inactivity.
|
||||
*/
|
||||
static void spi_hash_inactive_timeout(void)
|
||||
{
|
||||
spi_hash_disable();
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to set up the new SPI device after physical presence check.
|
||||
*/
|
||||
static void spi_hash_pp_done(void)
|
||||
{
|
||||
/* Acquire the bus */
|
||||
if (set_spi_bus_user(SPI_BUS_USER_HASH, 1)) {
|
||||
CPRINTS("spihdev: bus busy");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Clear previous enable if needed */
|
||||
if (spi_hash_device != USB_SPI_DISABLE)
|
||||
disable_ec_ap_spi();
|
||||
|
||||
/* Set up new device */
|
||||
if (new_device == USB_SPI_AP) {
|
||||
/* Stop the EC device, if it was previously active */
|
||||
spi_hash_stop_ec_device();
|
||||
|
||||
enable_ap_spi();
|
||||
} else {
|
||||
/* Force the EC into reset and enable EC SPI bus */
|
||||
assert_ec_rst();
|
||||
enable_ec_spi();
|
||||
|
||||
/*
|
||||
* If EC is headed into gang programmer mode, need to release
|
||||
* EC from reset after acquiring the bus. EC_FLASH_SELECT runs
|
||||
* to the EC's GP_SEL_ODL signal, which is what enables gang
|
||||
* programmer mode.
|
||||
*/
|
||||
if (new_gang_mode) {
|
||||
usleep(200);
|
||||
deassert_ec_rst();
|
||||
use_npcx_gang_mode = 1;
|
||||
}
|
||||
}
|
||||
|
||||
enable_spi_pinmux();
|
||||
spi_hash_device = new_device;
|
||||
|
||||
/* Start inactivity timer to turn hashing mode off */
|
||||
hook_call_deferred(&spi_hash_inactive_timeout_data,
|
||||
SPI_HASH_TIMEOUT_US);
|
||||
|
||||
CPRINTS("SPI hash device: %s",
|
||||
(spi_hash_device == USB_SPI_AP ? "AP" : "EC"));
|
||||
}
|
||||
|
||||
static int command_spi_hash_set_device(int argc, char **argv)
|
||||
{
|
||||
new_device = spi_hash_device;
|
||||
new_gang_mode = 0;
|
||||
|
||||
/* See if user wants to change the hash device */
|
||||
if (argc >= 2) {
|
||||
if (!strcasecmp(argv[1], "AP"))
|
||||
new_device = USB_SPI_AP;
|
||||
else if (!strcasecmp(argv[1], "EC"))
|
||||
new_device = USB_SPI_EC;
|
||||
else if (!strcasecmp(argv[1], "disable"))
|
||||
new_device = USB_SPI_DISABLE;
|
||||
else
|
||||
return EC_ERROR_PARAM1;
|
||||
}
|
||||
|
||||
/* Check for whether to use NPCX gang programmer mode */
|
||||
if (argc >= 3) {
|
||||
if (new_device == USB_SPI_EC && !strcasecmp(argv[2], "gang"))
|
||||
new_gang_mode = 1;
|
||||
else
|
||||
return EC_ERROR_PARAM2;
|
||||
}
|
||||
|
||||
if (new_device != spi_hash_device) {
|
||||
/* If we don't have permission, only allow disabling */
|
||||
if (new_device != USB_SPI_DISABLE &&
|
||||
!(ccd_is_cap_enabled(CCD_CAP_FLASH_READ)))
|
||||
return EC_ERROR_ACCESS_DENIED;
|
||||
|
||||
if (new_device == USB_SPI_DISABLE) {
|
||||
/* Disable SPI hashing */
|
||||
return spi_hash_disable();
|
||||
}
|
||||
|
||||
if (spi_hash_device == USB_SPI_DISABLE &&
|
||||
!(ccd_is_cap_enabled(CCD_CAP_AP_FLASH) &&
|
||||
ccd_is_cap_enabled(CCD_CAP_EC_FLASH))) {
|
||||
/*
|
||||
* We were disabled, and CCD does not grant permission
|
||||
* to both flash chips. So we need physical presence
|
||||
* to take the SPI bus. That prevents a malicious
|
||||
* peripheral from using this to reset the device.
|
||||
*
|
||||
* Technically, we could track the chips separately,
|
||||
* and only require physical presence the first time we
|
||||
* check a chip which CCD doesn't grant access to. But
|
||||
* that's more bookkeeping, so for now the only way to
|
||||
* skip physical presence is to have access to both.
|
||||
*/
|
||||
return physical_detect_start(0, spi_hash_pp_done);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're still here, we already own the SPI bus, and are
|
||||
* changing which chip we're looking at. Update hash device
|
||||
* directly; no new physical presence required.
|
||||
*/
|
||||
spi_hash_pp_done();
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
ccprintf("SPI hash device: %s\n",
|
||||
(spi_hash_device ?
|
||||
(spi_hash_device == USB_SPI_AP ? "AP" : "EC") : "disable"));
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
|
||||
static int command_spi_hash(int argc, char **argv)
|
||||
{
|
||||
HASH_CTX sha;
|
||||
int offset = -1;
|
||||
int chunk_size = SPI_HASH_CHUNK_SIZE;
|
||||
int size = 256;
|
||||
int rv = EC_SUCCESS;
|
||||
uint8_t data[SPI_HASH_CHUNK_SIZE];
|
||||
int dump = 0;
|
||||
int chunks = 0;
|
||||
int i;
|
||||
|
||||
/* Handle setting/printing the active device */
|
||||
if (argc == 1 ||
|
||||
!strcasecmp(argv[1], "AP") ||
|
||||
!strcasecmp(argv[1], "EC") ||
|
||||
!strcasecmp(argv[1], "disable"))
|
||||
return command_spi_hash_set_device(argc, argv);
|
||||
|
||||
/* Fail if we don't own the bus */
|
||||
if (get_spi_bus_user() != SPI_BUS_USER_HASH) {
|
||||
ccprintf("SPI hash not enabled\n");
|
||||
return EC_ERROR_ACCESS_DENIED;
|
||||
}
|
||||
|
||||
/* Bump inactivity timer to turn hashing mode off */
|
||||
hook_call_deferred(&spi_hash_inactive_timeout_data,
|
||||
SPI_HASH_TIMEOUT_US);
|
||||
|
||||
/* Parse args */
|
||||
// TODO: parse offset and size directly, since we want them both
|
||||
rv = parse_offset_size(argc, argv, 1, &offset, &size);
|
||||
if (rv)
|
||||
return rv;
|
||||
if (argc > 3 && !strcasecmp(argv[3], "dump"))
|
||||
dump = 1;
|
||||
|
||||
if (size < 0 || size > MAX_SPI_HASH_SIZE)
|
||||
return EC_ERROR_INVAL;
|
||||
|
||||
DCRYPTO_SHA256_init(&sha, 0);
|
||||
|
||||
for (chunks = 0; size > 0; chunks++) {
|
||||
int this_chunk = MIN(size, chunk_size);
|
||||
|
||||
/* Read the data */
|
||||
rv = spi_read_chunk(data, offset, this_chunk);
|
||||
if (rv) {
|
||||
ccprintf("Read error at 0x%x\n", offset);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* Update hash */
|
||||
HASH_update(&sha, data, this_chunk);
|
||||
|
||||
if (dump) {
|
||||
/* Also dump it */
|
||||
for (i = 0; i < this_chunk; i++) {
|
||||
if ((offset + i) % 16) {
|
||||
ccprintf(" %02x", data[i]);
|
||||
} else {
|
||||
ccprintf("\n%08x: %02x",
|
||||
offset + i, data[i]);
|
||||
cflush();
|
||||
}
|
||||
}
|
||||
ccputs("\n");
|
||||
msleep(1);
|
||||
} else {
|
||||
/* Print often at first then slow down */
|
||||
if (chunks < 16 || !(chunks % 64)) {
|
||||
ccputs(".");
|
||||
msleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
size -= this_chunk;
|
||||
offset += this_chunk;
|
||||
}
|
||||
|
||||
if (!dump) {
|
||||
cflush(); /* Make sure there's space for the hash to print */
|
||||
ccputs("\n");
|
||||
}
|
||||
|
||||
ccprintf("Hash = %.32h\n", HASH_final(&sha));
|
||||
return EC_SUCCESS;
|
||||
}
|
||||
DECLARE_SAFE_CONSOLE_COMMAND(spihash, command_spi_hash,
|
||||
"ap | ec [gang] | disable | <offset> <size> [dump]",
|
||||
"Hash SPI flash");
|
||||
|
||||
@@ -141,6 +141,7 @@ static const struct ccd_capability_info cap_info[CCD_CAP_COUNT] = {
|
||||
{"BatteryBypassPP", CCD_CAP_STATE_ALWAYS},
|
||||
{"UpdateNoTPMWipe", CCD_CAP_STATE_ALWAYS},
|
||||
{"I2C", CCD_CAP_STATE_IF_OPENED},
|
||||
{"FlashRead", CCD_CAP_STATE_ALWAYS},
|
||||
};
|
||||
|
||||
static const char *ccd_state_names[CCD_STATE_COUNT] = {
|
||||
|
||||
@@ -94,6 +94,9 @@ enum ccd_capability {
|
||||
/* Access to I2C via USB */
|
||||
CCD_CAP_I2C = 15,
|
||||
|
||||
/* Read-only access to hash or dump EC or AP flash */
|
||||
CCD_CAP_FLASH_READ = 16,
|
||||
|
||||
/* Number of currently defined capabilities */
|
||||
CCD_CAP_COUNT
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user