/* 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. */ /* Non-volatile storage routines. */ #include "sysincludes.h" #include "crc8.h" #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_DISABLE_DEV_REQUEST 0x40 #define BOOT_OPROM_NEEDED 0x20 #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 DEV_BOOT_SIGNED_ONLY_MASK 0x02 #define DEV_BOOT_LEGACY_MASK 0x04 #define TPM_FLAGS_OFFSET 5 #define TPM_CLEAR_OWNER_REQUEST 0x01 #define TPM_CLEAR_OWNER_DONE 0x02 #define RECOVERY_SUBCODE_OFFSET 6 #define KERNEL_FIELD_OFFSET 11 #define CRC_OFFSET 15 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); 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_RECOVERY_SUBCODE: *dest = raw[RECOVERY_SUBCODE_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_DEV_BOOT_USB: *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_USB_MASK ? 1 : 0); return 0; case VBNV_DEV_BOOT_LEGACY: *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_LEGACY_MASK ? 1 : 0); return 0; case VBNV_DEV_BOOT_SIGNED_ONLY: *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_SIGNED_ONLY_MASK ? 1 : 0); return 0; case VBNV_DISABLE_DEV_REQUEST: *dest = (raw[BOOT_OFFSET] & BOOT_DISABLE_DEV_REQUEST ? 1 : 0); return 0; case VBNV_OPROM_NEEDED: *dest = (raw[BOOT_OFFSET] & BOOT_OPROM_NEEDED ? 1 : 0); return 0; case VBNV_CLEAR_TPM_OWNER_REQUEST: *dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_REQUEST ? 1 : 0); return 0; case VBNV_CLEAR_TPM_OWNER_DONE: *dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_DONE ? 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 not changing the value, don't 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_RECOVERY_SUBCODE: raw[RECOVERY_SUBCODE_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_DEV_BOOT_USB: if (value) raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_USB_MASK; else raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_USB_MASK; break; case VBNV_DEV_BOOT_LEGACY: if (value) raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_LEGACY_MASK; else raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_LEGACY_MASK; break; case VBNV_DEV_BOOT_SIGNED_ONLY: if (value) raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_SIGNED_ONLY_MASK; else raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_SIGNED_ONLY_MASK; break; case VBNV_DISABLE_DEV_REQUEST: if (value) raw[BOOT_OFFSET] |= BOOT_DISABLE_DEV_REQUEST; else raw[BOOT_OFFSET] &= ~BOOT_DISABLE_DEV_REQUEST; break; case VBNV_OPROM_NEEDED: if (value) raw[BOOT_OFFSET] |= BOOT_OPROM_NEEDED; else raw[BOOT_OFFSET] &= ~BOOT_OPROM_NEEDED; break; case VBNV_CLEAR_TPM_OWNER_REQUEST: if (value) raw[TPM_FLAGS_OFFSET] |= TPM_CLEAR_OWNER_REQUEST; else raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_REQUEST; break; case VBNV_CLEAR_TPM_OWNER_DONE: if (value) raw[TPM_FLAGS_OFFSET] |= TPM_CLEAR_OWNER_DONE; else raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_DONE; break; default: return 1; } /* Need to regenerate CRC, since the value changed. */ context->regenerate_crc = 1; return 0; }