vboot: add vbnv flash driver

this adds a driver for vboot to read and write nvdata in spi flash.
it's assumed that flash contents are erased to 1-bits and write
operations can only change 1-bits to 0-bits.

when all nvram space is used, the driver will erase the whole block
and start the next write from the beginning.

BUG=chrome-os-partner:32774
BRANCH=ToT
TEST=Built for cosmos.

Change-Id: I40858f847151aa0770e1101e905476d270550f60
Signed-off-by: Stefan Reinauer <reinauer@chromium.org>
Original-Commit-Id: 09713828b7b0cabd13a08de3f34e32bc4dbef4a4
Original-Change-Id: Ia9049f342b21fa4c289cb7b9254ab89ec1ef1699
Original-Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/226525
Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/9229
Tested-by: build bot (Jenkins)
Reviewed-by: David Hendricks <dhendrix@chromium.org>
Reviewed-by: Paul Menzel <paulepanter@users.sourceforge.net>
This commit is contained in:
Daisuke Nojiri 2014-10-29 11:18:11 -07:00 committed by Patrick Georgi
parent 2af67c9878
commit 2b59a838b4
1 changed files with 161 additions and 0 deletions

View File

@ -17,12 +17,173 @@
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
#include <2api.h>
#include <cbfs.h>
#include <cbfs_core.h>
#include <console/console.h>
#include <spi_flash.h>
#include <string.h>
#include <vboot_nvstorage.h>
#include "chromeos.h" #include "chromeos.h"
#if IS_ENABLED(CONFIG_VBOOT_VERIFY_FIRMWARE)
#define BLOB_SIZE VBNV_BLOCK_SIZE
#elif IS_ENABLED(CONFIG_VBOOT2_VERIFY_FIRMWARE)
#define BLOB_SIZE VB2_NVDATA_SIZE
#else
#error unable to determine BLOB_SIZE
#endif
/* FMAP descriptor of the NVRAM area */
static struct vboot_region nvram_region;
/* offset of the current nvdata in nvram */
static int blob_offset = -1;
/* cache of the current nvdata */
static uint8_t cache[BLOB_SIZE];
/* spi_flash struct used when saving data */
static struct spi_flash *spi_flash = NULL;
/*
* This code assumes that flash is erased to 1-bits, and write operations can
* only change 1-bits to 0-bits. So if the new contents only change 1-bits to
* 0-bits, we can reuse the current blob.
*/
static inline uint8_t erase_value(void)
{
return 0xff;
}
static inline int can_overwrite(uint8_t current, uint8_t new)
{
return (current & new) == new;
}
static inline int is_initialized(void)
{
return blob_offset >= 0;
}
static int init_vbnv(void)
{
uint8_t buf[BLOB_SIZE];
uint8_t empty_blob[BLOB_SIZE];
int offset;
int i;
vboot_locate_region("RW_NVRAM", &nvram_region);
if (nvram_region.size < BLOB_SIZE) {
printk(BIOS_ERR, "%s: failed to locate NVRAM\n", __func__);
return 1;
}
/* Prepare an empty blob to compare against. */
for (i = 0; i < BLOB_SIZE; i++)
empty_blob[i] = erase_value();
/*
* after the loop, offset is supposed to point the blob right before the
* first empty blob, the last blob in the nvram if there is no empty
* blob, or 0 if the nvram has never been used.
*/
for (i = 0, offset = 0; i <= nvram_region.size - BLOB_SIZE;
i += BLOB_SIZE) {
if (vboot_get_region(i, BLOB_SIZE, buf) == NULL) {
printk(BIOS_ERR, "failed to read nvdata\n");
return 1;
}
if (!memcmp(buf, empty_blob, BLOB_SIZE))
break;
offset = i;
}
/* reread the nvdata and write it to the cache */
if (vboot_get_region(offset, BLOB_SIZE, cache) == NULL) {
printk(BIOS_ERR, "failed to read nvdata\n");
return 1;
}
blob_offset = offset;
return 0;
}
static int vbnv_flash_probe(void)
{
if (!spi_flash) {
spi_flash = spi_flash_probe(CONFIG_BOOT_MEDIA_SPI_BUS, 0);
if (!spi_flash) {
printk(BIOS_ERR, "failed to probe spi flash\n");
return 1;
}
}
return 0;
}
static int erase_nvram(void)
{
if (vbnv_flash_probe())
return 1;
if (spi_flash->erase(spi_flash, nvram_region.offset_addr,
nvram_region.size)) {
printk(BIOS_ERR, "failed to erase nvram\n");
return 1;
}
printk(BIOS_INFO, "nvram is cleared\n");
return 0;
}
void read_vbnv(uint8_t *vbnv_copy) void read_vbnv(uint8_t *vbnv_copy)
{ {
if (!is_initialized())
if (init_vbnv())
return; /* error */
memcpy(vbnv_copy, cache, BLOB_SIZE);
} }
void save_vbnv(const uint8_t *vbnv_copy) void save_vbnv(const uint8_t *vbnv_copy)
{ {
int new_offset = blob_offset;
int i;
if (!is_initialized())
if (init_vbnv())
return; /* error */
/* Bail out if there have been no changes. */
if (!memcmp(vbnv_copy, cache, BLOB_SIZE))
return;
/* See if we can overwrite the current blob with the new one */
for (i = 0; i < BLOB_SIZE; i++) {
if (!can_overwrite(cache[i], vbnv_copy[i])) {
/* unable to overwrite. need to use the next blob */
new_offset += BLOB_SIZE;
if (new_offset > nvram_region.size - BLOB_SIZE) {
if (erase_nvram())
return; /* error */
new_offset = 0;
}
break;
}
}
if (vbnv_flash_probe())
return; /* error */
if (spi_flash->write(spi_flash, new_offset,
BLOB_SIZE, vbnv_copy) != BLOB_SIZE) {
printk(BIOS_ERR, "failed to write nvdata\n");
return; /* error */
}
/* write was successful. safely move pointer forward */
blob_offset = new_offset;
memcpy(cache, vbnv_copy, BLOB_SIZE);
return;
} }