| /* |
| * 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; |
| } |