/* 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_MASK 0x0F #define RECOVERY_OFFSET 2 #define LOCALIZATION_OFFSET 3 #define DEV_FLAGS_OFFSET 4 #define DEV_BOOT_USB_MASK 0x01 #define FIRMWARE_FLAGS_OFFSET 5 #define FIRMWARE_TEST_ERR_FUNC_MASK 0x38 #define FIRMWARE_TEST_ERR_FUNC_SHIFT 3 #define FIRMWARE_TEST_ERR_NUM_MASK 0x07 #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, VBNV_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_MASK; 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; case VBNV_TEST_ERROR_FUNC: *dest = (raw[FIRMWARE_FLAGS_OFFSET] & FIRMWARE_TEST_ERR_FUNC_MASK) >> FIRMWARE_TEST_ERR_FUNC_SHIFT; return 0; case VBNV_TEST_ERROR_NUM: *dest = raw[FIRMWARE_FLAGS_OFFSET] & FIRMWARE_TEST_ERR_NUM_MASK; return 0; case VBNV_DEV_BOOT_USB: *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_USB_MASK ? 1 : 0); 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, ¤t) && 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: /* Clip to valid range. */ if (value > BOOT_TRY_B_COUNT_MASK) value = BOOT_TRY_B_COUNT_MASK; raw[BOOT_OFFSET] &= ~BOOT_TRY_B_COUNT_MASK; raw[BOOT_OFFSET] |= (uint8_t)value; break; case VBNV_RECOVERY_REQUEST: /* Map values outside the valid range to the legacy reason, since we * can't determine if we're called from kernel or user mode. */ if (value > 0xFF) value = VBNV_RECOVERY_LEGACY; raw[RECOVERY_OFFSET] = (uint8_t)value; break; case VBNV_LOCALIZATION_INDEX: /* Map values outside the valid range to the default index. */ if (value > 0xFF) value = 0; 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; case VBNV_TEST_ERROR_FUNC: raw[FIRMWARE_FLAGS_OFFSET] &= ~FIRMWARE_TEST_ERR_FUNC_MASK; raw[FIRMWARE_FLAGS_OFFSET] |= (value << FIRMWARE_TEST_ERR_FUNC_SHIFT) & FIRMWARE_TEST_ERR_FUNC_MASK; break; case VBNV_TEST_ERROR_NUM: raw[FIRMWARE_FLAGS_OFFSET] &= ~FIRMWARE_TEST_ERR_NUM_MASK; raw[FIRMWARE_FLAGS_OFFSET] |= (value & FIRMWARE_TEST_ERR_NUM_MASK); break; case VBNV_DEV_BOOT_USB: if (value) raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_USB_MASK; else raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_USB_MASK; break; default: return 1; } /* Need to regenerate CRC, since the value changed. */ context->regenerate_crc = 1; return 0; }