summaryrefslogtreecommitdiff
path: root/firmware/lib/vboot_nvstorage.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/lib/vboot_nvstorage.c')
-rw-r--r--firmware/lib/vboot_nvstorage.c186
1 files changed, 186 insertions, 0 deletions
diff --git a/firmware/lib/vboot_nvstorage.c b/firmware/lib/vboot_nvstorage.c
new file mode 100644
index 00000000..81816ac4
--- /dev/null
+++ b/firmware/lib/vboot_nvstorage.c
@@ -0,0 +1,186 @@
+/* Copyright (c) 2011 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.
+ */
+
+/* Non-volatile storage routines.
+ */
+
+#include "utility.h"
+#include "vboot_common.h"
+#include "vboot_nvstorage.h"
+
+/* Constants for NV storage. We use this rather than structs and
+ * bitfields so the data format is consistent across platforms and
+ * compilers. */
+#define HEADER_OFFSET 0
+#define HEADER_MASK 0xC0
+#define HEADER_SIGNATURE 0x40
+#define HEADER_FIRMWARE_SETTINGS_RESET 0x20
+#define HEADER_KERNEL_SETTINGS_RESET 0x10
+
+#define BOOT_OFFSET 1
+#define BOOT_DEBUG_RESET_MODE 0x80
+#define BOOT_TRY_B_COUNT 0x0F
+
+#define RECOVERY_OFFSET 2
+#define LOCALIZATION_OFFSET 3
+#define KERNEL_FIELD_OFFSET 11
+#define CRC_OFFSET 15
+
+
+/* Return CRC-8 of the data, using x^8 + x^2 + x + 1 polynomial. A
+ * table-based algorithm would be faster, but for only 15 bytes isn't
+ * worth the code size. */
+static uint8_t Crc8(const uint8_t* data, int len) {
+ unsigned crc = 0;
+ int i, j;
+
+ for (j = len; j; j--, data++) {
+ crc ^= (*data << 8);
+ for(i = 8; i; i--) {
+ if (crc & 0x8000)
+ crc ^= (0x1070 << 3);
+ crc <<= 1;
+ }
+ }
+
+ return (uint8_t)(crc >> 8);
+}
+
+
+int VbNvSetup(VbNvContext* context) {
+ uint8_t* raw = context->raw;
+
+ /* Nothing has changed yet. */
+ context->raw_changed = 0;
+ context->regenerate_crc = 0;
+
+ /* Check data for consistency */
+ if ((HEADER_SIGNATURE != (raw[HEADER_OFFSET] & HEADER_MASK))
+ || (Crc8(raw, CRC_OFFSET) != raw[CRC_OFFSET])) {
+
+ /* Data is inconsistent (bad CRC or header), so reset defaults */
+ Memset(raw, 0, NV_BLOCK_SIZE);
+ raw[HEADER_OFFSET] = (HEADER_SIGNATURE | HEADER_FIRMWARE_SETTINGS_RESET |
+ HEADER_KERNEL_SETTINGS_RESET);
+
+ /* Regenerate CRC on exit */
+ context->regenerate_crc = 1;
+ }
+
+ return 0;
+}
+
+
+int VbNvTeardown(VbNvContext* context) {
+
+ if (context->regenerate_crc) {
+ context->raw[CRC_OFFSET] = Crc8(context->raw, CRC_OFFSET);
+ context->regenerate_crc = 0;
+ context->raw_changed = 1;
+ }
+
+ return 0;
+}
+
+
+int VbNvGet(VbNvContext* context, VbNvParam param, uint32_t* dest) {
+ const uint8_t* raw = context->raw;
+
+ switch (param) {
+ case VBNV_FIRMWARE_SETTINGS_RESET:
+ *dest = (raw[HEADER_OFFSET] & HEADER_FIRMWARE_SETTINGS_RESET ? 1 : 0);
+ return 0;
+
+ case VBNV_KERNEL_SETTINGS_RESET:
+ *dest = (raw[HEADER_OFFSET] & HEADER_KERNEL_SETTINGS_RESET ? 1 : 0);
+ return 0;
+
+ case VBNV_DEBUG_RESET_MODE:
+ *dest = (raw[BOOT_OFFSET] & BOOT_DEBUG_RESET_MODE ? 1 : 0);
+ return 0;
+
+ case VBNV_TRY_B_COUNT:
+ *dest = raw[BOOT_OFFSET] & BOOT_TRY_B_COUNT;
+ return 0;
+
+ case VBNV_RECOVERY_REQUEST:
+ *dest = raw[RECOVERY_OFFSET];
+ return 0;
+
+ case VBNV_LOCALIZATION_INDEX:
+ *dest = raw[LOCALIZATION_OFFSET];
+ return 0;
+
+ case VBNV_KERNEL_FIELD:
+ *dest = (raw[KERNEL_FIELD_OFFSET]
+ | (raw[KERNEL_FIELD_OFFSET + 1] << 8)
+ | (raw[KERNEL_FIELD_OFFSET + 2] << 16)
+ | (raw[KERNEL_FIELD_OFFSET + 3] << 24));
+ return 0;
+
+ default:
+ return 1;
+ }
+}
+
+
+int VbNvSet(VbNvContext* context, VbNvParam param, uint32_t value) {
+ uint8_t* raw = context->raw;
+ uint32_t current;
+
+ /* If we're not changing the value, we don't need to regenerate the CRC. */
+ if (0 == VbNvGet(context, param, &current) && current == value)
+ return 0;
+
+ switch (param) {
+ case VBNV_FIRMWARE_SETTINGS_RESET:
+ if (value)
+ raw[HEADER_OFFSET] |= HEADER_FIRMWARE_SETTINGS_RESET;
+ else
+ raw[HEADER_OFFSET] &= ~HEADER_FIRMWARE_SETTINGS_RESET;
+ break;
+
+ case VBNV_KERNEL_SETTINGS_RESET:
+ if (value)
+ raw[HEADER_OFFSET] |= HEADER_KERNEL_SETTINGS_RESET;
+ else
+ raw[HEADER_OFFSET] &= ~HEADER_KERNEL_SETTINGS_RESET;
+ break;
+
+ case VBNV_DEBUG_RESET_MODE:
+ if (value)
+ raw[BOOT_OFFSET] |= BOOT_DEBUG_RESET_MODE;
+ else
+ raw[BOOT_OFFSET] &= BOOT_DEBUG_RESET_MODE;
+ break;
+
+ case VBNV_TRY_B_COUNT:
+ raw[BOOT_OFFSET] &= ~BOOT_TRY_B_COUNT;
+ raw[BOOT_OFFSET] |= (uint8_t)(value & BOOT_TRY_B_COUNT);
+ break;
+
+ case VBNV_RECOVERY_REQUEST:
+ raw[RECOVERY_OFFSET] = (uint8_t)value;
+ break;
+
+ case VBNV_LOCALIZATION_INDEX:
+ raw[LOCALIZATION_OFFSET] = (uint8_t)value;
+ break;
+
+ case VBNV_KERNEL_FIELD:
+ raw[KERNEL_FIELD_OFFSET] = (uint8_t)(value);
+ raw[KERNEL_FIELD_OFFSET + 1] = (uint8_t)(value >> 8);
+ raw[KERNEL_FIELD_OFFSET + 2] = (uint8_t)(value >> 16);
+ raw[KERNEL_FIELD_OFFSET + 3] = (uint8_t)(value >> 24);
+ break;
+
+ default:
+ return 1;
+ }
+
+ /* Need to regenerate CRC, since the value changed. */
+ context->regenerate_crc = 1;
+ return 0;
+}