blob: fdc4c5c421b2d2f6b835f2b2bc97d40cf4b76d2e [file] [log] [blame]
/*
* Copyright 2014 Google Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but without any warranty; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <libpayload.h>
#include <config.h>
#include <image/fmap.h>
#include <drivers/flash/flash.h>
#include <vboot_api.h>
/*
* NVRAM storage in flash uses a block of flash memory to represent the NVRAM
* blob. Typical flash memory allows changing of individual bits from one to
* zero. Changing bits from zero to one requires an erase operation, which
* affects entire blocks of storage.
*
* In a typical case the last non-erased blob of CONFIG_NVRAM_BLOCK_SIZE bytes
* in the dedicated block is considered the current NVRAM contents. If there
* is a need to change the NVRAM contents, the next blob worth of bytes is
* written. It becomes the last non-erased blob, which is by definition the
* current NVRAM contents.
*
* If the entire dedicated block is empty, the first blob is used as the
* NVRAM. It will be considered invalid and overwritten by vboot as required.
*
* If there is no room in the dedicated block to store a new blob - the NVRAM
* write operation would fail.
*
* This scheme expects the user space application to manage the allocated
* block, erasing it and initializing the lowest blob with the current NVRAM
* contents once it gets close to capacity.
*
*/
/* FMAP descriptor of the NVRAM area */
static FmapArea nvram_area_descriptor;
/* Pointer to the NVRAM area in the flash mirror buffer. */
static uint8_t *nvram_area_in_flash;
/* Offset of the actual NVRAM blob offset in the NVRAM block. */
static int nvram_blob_offset;
/* Local cache of the NVRAM blob. */
static uint8_t nvram_cache[CONFIG_NV_STORAGE_FLASH_BLOB_SIZE];
VbError_t VbExNvStorageRead(uint8_t *buf)
{
memcpy(buf, nvram_cache, sizeof(nvram_cache));
return VBERROR_SUCCESS;
}
VbError_t VbExNvStorageWrite(const uint8_t *buf)
{
int i;
/* Bail out if there have been no changes. */
if (!memcmp(buf, nvram_cache, sizeof(nvram_cache)))
return VBERROR_SUCCESS;
/* See if we can overwrite the current blob with the new one. */
for (i = 0; i < sizeof(nvram_cache); i++)
if ((nvram_cache[i] & buf[i]) != buf[i])
break;
if (i != sizeof(nvram_cache)) {
int new_blob_offset;
/*
* Won't be able to overwrite, need to use the next blob,
* let's see if it is available.
*/
new_blob_offset = nvram_blob_offset + sizeof(nvram_cache);
if ((new_blob_offset + sizeof(nvram_cache)) >
nvram_area_descriptor.size)
return VBERROR_INVALID_PARAMETER;
nvram_blob_offset = new_blob_offset;
}
if (flash_write(nvram_area_descriptor.offset + nvram_blob_offset,
sizeof(nvram_cache), buf) != sizeof(nvram_cache))
return VBERROR_UNKNOWN;
memcpy(nvram_cache, buf, sizeof(nvram_cache));
return VBERROR_SUCCESS;
}
int flash_nvram_init(void)
{
int area_offset, i, prev_offset, size_limit;
uint8_t empty_nvram_block[sizeof(nvram_cache)];
if (fmap_find_area("RW_NVRAM", &nvram_area_descriptor)) {
printf("%s: failed to find NVRAM area\n", __func__);
return -1;
}
nvram_area_in_flash = flash_read(nvram_area_descriptor.offset,
nvram_area_descriptor.size);
if (!nvram_area_in_flash) {
printf("%s: failed to read NVRAM area\n", __func__);
return -1;
}
/* Prepare an empty NVRAM block to compare against. */
for (i = 0; i < sizeof(nvram_cache); i++)
empty_nvram_block[i] = 0xff;
/*
* Now find the first completely empty NVRAM blob. The actual NVRAM
* blob will be right below it.
*/
size_limit = nvram_area_descriptor.size - sizeof(nvram_cache);
for (area_offset = 0, prev_offset = 0;
area_offset <= size_limit;
area_offset += sizeof(nvram_cache)) {
if (!memcmp(nvram_area_in_flash + area_offset,
empty_nvram_block,
sizeof(nvram_cache)))
break;
prev_offset = area_offset;
}
memcpy(nvram_cache,
nvram_area_in_flash + prev_offset,
sizeof(nvram_cache));
nvram_blob_offset = prev_offset;
return 0;
}
int nvstorage_flash_get_offet(void)
{
return nvram_blob_offset;
}
int nvstorage_flash_get_blob_size(void)
{
return sizeof(nvram_cache);
}