blob: 714bf56f656c6f69956a989da0d3e74b90449552 [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 <stdio.h>
#include <stdlib.h>
#include <vboot_api.h>
#include "base/xalloc.h"
#include "drivers/storage/storage.h"
#include "vboot/board.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.
*/
// 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[VBNV_BLOCK_SIZE];
static StorageOps *nvram_storage;
static int nvram_storage_size;
static int flash_nvram_init(void)
{
StorageOps *storage = board_storage_vboot_nvstorage();
nvram_storage_size = storage_size(nvram_storage);
if (nvram_storage_size < 0)
return -1;
uint8_t *data = xmalloc(nvram_storage_size);
if (storage_read(nvram_storage, data, 0, nvram_storage_size)) {
free(data);
return -1;
}
// Prepare an empty NVRAM block to compare against.
uint8_t empty_block[sizeof(nvram_cache)];
memset(empty_block, 0xff, sizeof(empty_block));
// Find the first completely empty NVRAM blob. The actual NVRAM
// blob will be right before it.
int last_offset = 0;
const int size = sizeof(nvram_cache);
for (int offset = size; offset < nvram_storage_size; offset += size) {
if (!memcmp(data + offset, empty_block, size))
break;
last_offset = offset;
}
nvram_storage = storage;
nvram_blob_offset = last_offset;
memcpy(nvram_cache, data + last_offset, size);
free(data);
return 0;
}
VbError_t VbExNvStorageRead(uint8_t *buf)
{
if (!nvram_storage && flash_nvram_init())
return VBERROR_UNKNOWN;
memcpy(buf, nvram_cache, sizeof(nvram_cache));
return VBERROR_SUCCESS;
}
VbError_t VbExNvStorageWrite(const uint8_t *buf)
{
const int cache_size = sizeof(nvram_cache);
if (!nvram_storage && flash_nvram_init())
return VBERROR_UNKNOWN;
// Bail out if there have been no changes.
if (!memcmp(buf, nvram_cache, cache_size))
return VBERROR_SUCCESS;
// See if we can overwrite the current blob with the new one.
int i;
for (i = 0; i < cache_size; i++)
if ((nvram_cache[i] & buf[i]) != buf[i])
break;
if (i != cache_size) {
// We won't be able to update the existing entry. Check if
// the next is available.
int new_offset = nvram_blob_offset + cache_size;
if (new_offset >= nvram_storage_size) {
// Nvram is full, so erase it.
uint8_t *empty = xmalloc(nvram_storage_size);
memset(empty, 0xff, nvram_storage_size);
if (storage_write(nvram_storage, empty, 0,
nvram_storage_size)) {
free(empty);
return VBERROR_UNKNOWN;
}
free(empty);
new_offset = 0;
}
nvram_blob_offset = new_offset;
}
if (storage_write(nvram_storage, buf, nvram_blob_offset, cache_size))
return VBERROR_UNKNOWN;
memcpy(nvram_cache, buf, cache_size);
return VBERROR_SUCCESS;
}
int nvstorage_flash_get_offset(void)
{
return nvram_blob_offset;
}