summaryrefslogtreecommitdiff
path: root/cgpt/flash_ts.c
diff options
context:
space:
mode:
Diffstat (limited to 'cgpt/flash_ts.c')
-rw-r--r--cgpt/flash_ts.c423
1 files changed, 0 insertions, 423 deletions
diff --git a/cgpt/flash_ts.c b/cgpt/flash_ts.c
deleted file mode 100644
index f37c4acc..00000000
--- a/cgpt/flash_ts.c
+++ /dev/null
@@ -1,423 +0,0 @@
-/* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-/* *** THIS CODE HAS NOT BEEN SECURITY REVIEWED ***
- * It lives in the firmware directory because that's where it needs to go
- * eventually, but at the moment it is used only by usermode tools.
- * Security review must be completed before this code is used in the
- * firmware.
- * See issue 246680
- */
-
-#include "flash_ts.h"
-
-#include <errno.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "utility.h"
-
-// These match the linux driver
-#define FLASH_TS_MAGIC 0x53542a46
-
-#define FLASH_TS_HEADER_SIZE 16
-#define FLASH_TS_MAX_SIZE 16384
-#define FLASH_TS_MAX_ELEMENT_SIZE (FLASH_TS_MAX_SIZE - FLASH_TS_HEADER_SIZE)
-
-typedef struct {
- uint32_t magic;
- uint32_t crc;
- uint32_t length;
- uint32_t version;
- char data[FLASH_TS_MAX_ELEMENT_SIZE];
-} __attribute__((packed)) flash_ts;
-
-typedef struct {
- size_t start_block; // Partition start offset (in erase blocks)
- size_t end_block; // Partition end offset (in erase blocks)
- size_t chunk_size; // Minimum element size
- size_t pages_per_block, chunks_per_block, pages_per_chunk;
- nand_geom nand;
-
- size_t cached_block;
- size_t current_block;
-
- flash_ts current;
- flash_ts temp;
-} flash_ts_state;
-
-
-static flash_ts_state state;
-
-size_t pow2(size_t x) {
- size_t v = 1;
- while (v < x)
- v <<= 1;
- return v;
-}
-
-static inline uint32_t flash_ts_crc(const flash_ts *cache)
-{
- const unsigned char *p;
- uint32_t crc = 0;
- size_t len;
-
- /* skip magic and crc fields */
- len = cache->length + 2 * sizeof(uint32_t);
- p = (const unsigned char*)&cache->length;
-
- while (len--) {
- int i;
-
- crc ^= *p++;
- for (i = 0; i < 8; i++)
- crc = (crc >> 1) ^ ((crc & 1) ? 0xedb88320 : 0);
- }
- return crc ^ ~0;
-}
-
-static inline int flash_ts_check_crc(const flash_ts *ts) {
- return ts->crc == flash_ts_crc(ts);
-}
-
-static int is_blank(const void *ptr, size_t sz) {
- const unsigned char *p = (const unsigned char*)ptr;
- const unsigned char *end = p + sz;
- while (p < end)
- if (*p++ != 0xff)
- return 0;
- return 1;
-}
-
-static int is_pow2(size_t v) {
- return v && (v & (v - 1)) == 0;
-}
-
-/* Scan the entire partition to find the latest version */
-static void flash_ts_scan_partition(flash_ts_state *ts) {
- size_t block;
-
- for (block = ts->start_block; block < ts->end_block; block++) {
- if (!nand_is_bad_block(&ts->nand, block)) {
- size_t chunk;
- size_t page_base = block * ts->pages_per_block;
-
- for (chunk = 0; chunk < ts->chunks_per_block;
- chunk++, page_base += ts->pages_per_chunk) {
- if (nand_read_page(&ts->nand, page_base,
- &ts->temp, sizeof(ts->temp))) {
- continue;
- }
- if (ts->temp.magic != FLASH_TS_MAGIC ||
- ts->temp.version <= ts->current.version ||
- !flash_ts_check_crc(&ts->temp)) {
- if (is_blank(&ts->temp, sizeof(ts->temp))) {
- // Since we only write sequentially, a blank chunk means no more
- // data in this block.
- break;
- }
- continue;
- }
-
- // It's good & newer than our current version
- VBDEBUG(("Found good version %d\n", ts->temp.version));
- ts->current_block = block;
- Memcpy(&ts->current, &ts->temp, sizeof(ts->current));
- }
- }
- }
-}
-
-static char *flash_ts_search(flash_ts *ts, const char *key) {
- char *str = &ts->data[0];
- size_t keylen = strlen(key);
-
- while(*str && str + keylen < &ts->data[ts->length]) {
- // Format: name=value\0name2=value2\0 ... keyn=valuen\0\0
- if (!Memcmp(str, key, keylen) && str[keylen] == '=') {
- return &str[keylen + 1];
- } else {
- str += strlen(str) + 1; // Skip to next key
- }
- }
- return NULL;
-}
-
-static int flash_ts_find_writeable_chunk(flash_ts_state *ts, uint32_t block) {
- uint32_t page_base = block * ts->pages_per_block;
- uint32_t page_end = (block + 1) * ts->pages_per_block;
-
- for(; page_base < page_end; page_base += ts->pages_per_chunk) {
- if(!nand_read_page(&ts->nand, page_base,
- &ts->temp, sizeof(ts->temp))) {
- if (is_blank(&ts->temp, sizeof(ts->temp)))
- return page_base;
- }
- }
-
- return -1;
-}
-
-static int in_range(const flash_ts_state *ts, uint32_t block) {
- return block >= ts->start_block && block < ts->end_block;
-}
-
-static int flash_try_write(flash_ts_state *ts, uint32_t page) {
- return nand_write_page(&ts->nand, page, &ts->current, sizeof(ts->current)) ||
- nand_read_page(&ts->nand, page, &ts->temp, sizeof(ts->temp)) ||
- Memcmp(&ts->current, &ts->temp, sizeof(ts->current));
-}
-
-
-static int flash_ts_find_writeable_spot(flash_ts_state *ts,
- uint32_t *page_ofs) {
- uint32_t block;
- if (in_range(ts, ts->cached_block)) {
- // We have a starting position to scan from
- block = ts->cached_block;
- } else {
- block = ts->start_block;
- VBDEBUG(("Cached block not in range - starting from %u\n", block));
- }
- for (; block < ts->end_block; block++) {
- int chunk;
- if (nand_is_bad_block(&ts->nand, block)) {
- VBDEBUG(("Skipping bad block %u\n", block));
- continue;
- }
-
- chunk = flash_ts_find_writeable_chunk(ts, block);
- if (chunk < 0) {
- VBDEBUG(("No free chunks in block %u\n", block));
- continue;
- }
-
- VBDEBUG(("Free chunk %d in block %u\n", chunk, block));
- *page_ofs = chunk;
- ts->cached_block = block;
- return 0;
- }
- return -1;
-}
-
-static int flash_try_erase(flash_ts_state *ts, int block) {
- return nand_is_bad_block(&ts->nand, block) ||
- nand_erase_block(&ts->nand, block);
-}
-
-static int flash_erase_any_block(flash_ts_state *ts, uint32_t hint) {
- uint32_t block;
- for (block = hint; block < ts->end_block; block++) {
- if (!flash_try_erase(ts, block)) {
- ts->cached_block = block;
- VBDEBUG(("Erased block %u\n", block));
- return 0;
- }
- }
-
- if (hint > ts->end_block)
- hint = ts->end_block;
-
- for (block = ts->start_block; block < hint; block++) {
- if (!flash_try_erase(ts, block)) {
- ts->cached_block = block;
- VBDEBUG(("Erased block %u\n", block));
- return 0;
- }
- }
- return -1;
-}
-
-static int flash_ts_write(flash_ts_state *ts) {
- int passes = 3;
- uint32_t page;
-
-
- ts->cached_block = ts->current_block;
- ts->current.version++;
- ts->current.crc = flash_ts_crc(&ts->current);
- VBDEBUG(("flash_ts_write() - %u bytes, crc %08X\n",
- ts->current.length, ts->current.crc));
-
- while(passes--) {
- if (flash_ts_find_writeable_spot(ts, &page)) {
- if (ts->cached_block == ts->end_block) {
- uint32_t block;
-
- // Partition full!
- // Erase a block to get some space
- if (in_range(ts, ts->current_block) &&
- ts->current_block != ts->end_block - 1) {
- // We don't want to overwrite our good copy if we can avoid it.
- block = ts->current_block + 1;
- } else {
- block = ts->start_block;
- }
- VBDEBUG(("Partition full - begin erasing from block %u\n", block));
-
- // Erase block, and try again.
- if (flash_erase_any_block(ts, block)) {
- // Failed to erase anything, so abort.
- VBDEBUG(("All erases failed, aborting\n"));
- return -ENOMEM;
- }
- continue;
- } else {
- // Try again, re-scan everything.
- ts->cached_block = ts->end_block;
- continue;
- }
- }
-
- if (flash_try_write(ts, page)) {
- // Write failure, or read-back failure, try again with the next block.
- VBDEBUG(("Write failure, retry\n"));
- ts->cached_block++;
- continue;
- }
-
- VBDEBUG(("Successfully written v%u @ %u\n", ts->current.version, page));
- ts->current_block = ts->cached_block;
- return 0;
- }
-
- VBDEBUG(("Out of tries\n"));
- return -EAGAIN;
-}
-
-// Set value, returns 0 on success
-int flash_ts_set(const char *key, const char *value) {
- flash_ts *ts = &state.current;
- char *at;
- size_t keylen = strlen(key);
- size_t value_len = strlen(value);
-
- if (keylen == 0) {
- VBDEBUG(("0-length key - illegal\n"));
- return -1;
- }
-
- if (strchr(key, '=')) {
- VBDEBUG(("key contains '=' - illegal\n"));
- return -1;
- }
-
- Memcpy(&state.temp, &state.current, sizeof(state.temp));
-
- at = flash_ts_search(ts, key);
- if (at) {
- size_t old_value_len;
-
- // Already exists
- if (!strcmp(at, value)) {
- // No change
- VBDEBUG(("Values are the same, not writing\n"));
- return 0;
- }
-
- old_value_len = strlen(at);
- if (value_len == old_value_len) {
- // Overwrite it
- Memcpy(at, value, value_len);
- VBDEBUG(("Values are the same length, overwrite\n"));
- } else {
- // Remove it
- // if value_len == 0, then we're done
- // if value_len != old_value_len, then we do the append below
- char *src = at - (keylen + 1);
- char *end = &ts->data[ts->length];
- char *from = at + old_value_len + 1;
-
- VBDEBUG(("Delete old value\n"));
- memmove(src, from, end - from);
- ts->length -= (from-src);
- ts->data[ts->length - 1] = '\0';
- at = NULL; // Enter the append branch below
- }
- } else if (value_len == 0) {
- // Removing non-existent entry
- return 0;
- }
-
- if (!at && value_len > 0) {
- // Append it
-
- if (ts->length + keylen + 1 + value_len + 1 > FLASH_TS_MAX_ELEMENT_SIZE) {
- // Not enough space, restore previous
- VBDEBUG(("Not enough space to write %d data bytes\n", (int)value_len));
- Memcpy(&state.current, &state.temp, sizeof(state.temp));
- return -1;
- }
-
- VBDEBUG(("Append new value\n"));
- at = &ts->data[ts->length - 1];
- strcpy(at, key);
- at[keylen] = '=';
- strcpy(at + keylen + 1, value);
- ts->length += keylen + 1 + value_len + 1;
- ts->data[ts->length-1] = '\0';
- }
-
- return flash_ts_write(&state);
-}
-
-void flash_ts_get(const char *key, char *value, unsigned int size) {
- flash_ts_state *ts = &state;
- const char *at;
-
- at = flash_ts_search(&ts->current, key);
- if (at) {
- strncpy(value, at, size);
- } else {
- *value = '\0';
- }
-}
-
-int flash_ts_init(unsigned int start_block, unsigned int blocks,
- unsigned int szofpg, unsigned int szofblk,
- unsigned int szofsector, void *user) {
- flash_ts_state *ts = &state;
-
- if (!is_pow2(szofpg) || !is_pow2(szofblk) || !is_pow2(szofsector) ||
- szofsector > szofpg || szofpg > szofblk || blocks == 0)
- return -ENODEV;
-
- Memset(ts, 0, sizeof(*ts));
-
- // Page <= chunk <= block
- // Page is minimum writable unit
- // Chunk is actual write unit
- // Block is erase unit
- ts->start_block = start_block;
- ts->end_block = start_block + blocks;
- ts->pages_per_block = szofblk / szofpg;
-
- ts->nand.user = user;
- ts->nand.szofpg = szofpg;
- ts->nand.szofblk = szofblk;
- ts->nand.szofsector = szofsector;
-
- // Calculate our write size, this mirrors the linux driver's logic
- ts->chunk_size = pow2((sizeof(flash_ts) + szofpg - 1) & ~(szofpg - 1));
- if (!is_pow2(ts->chunk_size))
- return -ENODEV;
-
- ts->pages_per_chunk = ts->chunk_size / szofpg;
- if (ts->pages_per_chunk == 0 || ts->chunk_size > szofblk)
- return -ENODEV;
-
- ts->chunks_per_block = szofblk / ts->chunk_size;
-
- ts->current.version = 0;
- ts->current.length = 1;
- ts->current.magic = FLASH_TS_MAGIC;
- ts->current.crc = flash_ts_crc(&ts->current);
- ts->current.data[0] = '\0';
- ts->current_block = ts->end_block;
-
- flash_ts_scan_partition(ts);
-
- return 0;
-}