| /* 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 "2crc8.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. |
| * |
| * These constants must match the equivalent constants in 2lib/2nvstorage.c. |
| * (We currently don't share a common header file because we're tring to keep |
| * the two libs independent, and we hope to deprecate this one.) |
| */ |
| #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 HEADER_WIPEOUT 0x08 |
| |
| #define BOOT_OFFSET 1 |
| #define BOOT_DEBUG_RESET_MODE 0x80 |
| #define BOOT_DISABLE_DEV_REQUEST 0x40 |
| #define BOOT_OPROM_NEEDED 0x20 |
| #define BOOT_BACKUP_NVRAM 0x10 |
| #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 DEV_BOOT_FASTBOOT_FULL_CAP_MASK 0x08 |
| #define DEV_DEFAULT_BOOT_MASK 0x30 |
| #define DEV_DEFAULT_BOOT_SHIFT 4 /* Number of bits to shift */ |
| |
| #define TPM_FLAGS_OFFSET 5 |
| #define TPM_CLEAR_OWNER_REQUEST 0x01 |
| #define TPM_CLEAR_OWNER_DONE 0x02 |
| #define TPM_REBOOTED 0x04 |
| |
| #define RECOVERY_SUBCODE_OFFSET 6 |
| |
| #define BOOT2_OFFSET 7 |
| #define BOOT2_RESULT_MASK 0x03 |
| #define BOOT2_TRIED 0x04 |
| #define BOOT2_TRY_NEXT 0x08 |
| #define BOOT2_PREV_RESULT_MASK 0x30 |
| #define BOOT2_PREV_RESULT_SHIFT 4 /* Number of bits to shift result */ |
| #define BOOT2_PREV_TRIED 0x40 |
| |
| #define MISC_OFFSET 8 |
| #define MISC_UNLOCK_FASTBOOT 0x01 |
| #define MISC_BOOT_ON_AC_DETECT 0x02 |
| #define MISC_TRY_RO_SYNC 0x04 |
| #define MISC_BATTERY_CUTOFF_REQUEST 0x08 |
| |
| #define KERNEL_MAX_ROLLFORWARD1_OFFSET 9 /* Low bits */ |
| #define KERNEL_MAX_ROLLFORWARD2_OFFSET 10 |
| #define KERNEL_MAX_ROLLFORWARD3_OFFSET 13 |
| #define KERNEL_MAX_ROLLFORWARD4_OFFSET 14 /* High bits */ |
| |
| #define KERNEL_FIELD1_OFFSET 11 /* Low bits */ |
| #define KERNEL_FIELD2_OFFSET 12 /* Low bits */ |
| |
| #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)) |
| || (vb2_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] = vb2_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: |
| case VBNV_FW_TRY_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_FIELD1_OFFSET] |
| | (raw[KERNEL_FIELD2_OFFSET] << 8)); |
| 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_DEFAULT_BOOT: |
| *dest = (raw[DEV_FLAGS_OFFSET] & DEV_DEFAULT_BOOT_MASK) |
| >> DEV_DEFAULT_BOOT_SHIFT; |
| return 0; |
| |
| case VBNV_DEV_BOOT_SIGNED_ONLY: |
| *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_SIGNED_ONLY_MASK ? |
| 1 : 0); |
| return 0; |
| |
| case VBNV_DEV_BOOT_FASTBOOT_FULL_CAP: |
| *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_FASTBOOT_FULL_CAP_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; |
| |
| case VBNV_TPM_REQUESTED_REBOOT: |
| *dest = (raw[TPM_FLAGS_OFFSET] & TPM_REBOOTED ? 1 : 0); |
| return 0; |
| |
| case VBNV_BACKUP_NVRAM_REQUEST: |
| *dest = (raw[BOOT_OFFSET] & BOOT_BACKUP_NVRAM ? 1 : 0); |
| return 0; |
| |
| case VBNV_FW_TRY_NEXT: |
| *dest = (raw[BOOT2_OFFSET] & BOOT2_TRY_NEXT ? 1 : 0); |
| return 0; |
| |
| case VBNV_FW_TRIED: |
| *dest = (raw[BOOT2_OFFSET] & BOOT2_TRIED ? 1 : 0); |
| return 0; |
| |
| case VBNV_FW_RESULT: |
| *dest = raw[BOOT2_OFFSET] & BOOT2_RESULT_MASK; |
| return 0; |
| |
| case VBNV_FW_PREV_TRIED: |
| *dest = (raw[BOOT2_OFFSET] & BOOT2_PREV_TRIED ? 1 : 0); |
| return 0; |
| |
| case VBNV_FW_PREV_RESULT: |
| *dest = (raw[BOOT2_OFFSET] & BOOT2_PREV_RESULT_MASK) |
| >> BOOT2_PREV_RESULT_SHIFT; |
| return 0; |
| |
| case VBNV_FW_REQ_WIPEOUT: |
| *dest = (raw[HEADER_OFFSET] & HEADER_WIPEOUT) ? 1 : 0; |
| return 0; |
| |
| case VBNV_FASTBOOT_UNLOCK_IN_FW: |
| *dest = (raw[MISC_OFFSET] & MISC_UNLOCK_FASTBOOT) ? 1 : 0; |
| return 0; |
| |
| case VBNV_BOOT_ON_AC_DETECT: |
| *dest = (raw[MISC_OFFSET] & MISC_BOOT_ON_AC_DETECT) ? 1 : 0; |
| return 0; |
| |
| case VBNV_TRY_RO_SYNC: |
| *dest = (raw[MISC_OFFSET] & MISC_TRY_RO_SYNC) ? 1 : 0; |
| return 0; |
| |
| case VBNV_BATTERY_CUTOFF_REQUEST: |
| *dest = (raw[MISC_OFFSET] & MISC_BATTERY_CUTOFF_REQUEST) |
| ? 1 : 0; |
| return 0; |
| |
| case VBNV_KERNEL_MAX_ROLLFORWARD: |
| *dest = (raw[KERNEL_MAX_ROLLFORWARD1_OFFSET] |
| | (raw[KERNEL_MAX_ROLLFORWARD2_OFFSET] << 8) |
| | (raw[KERNEL_MAX_ROLLFORWARD3_OFFSET] << 16) |
| | (raw[KERNEL_MAX_ROLLFORWARD4_OFFSET] << 24)); |
| 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: |
| case VBNV_FW_TRY_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_FIELD1_OFFSET] = (uint8_t)(value); |
| raw[KERNEL_FIELD2_OFFSET] = (uint8_t)(value >> 8); |
| 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_DEFAULT_BOOT: |
| /* Map out of range values to boot disk */ |
| if (value > (DEV_DEFAULT_BOOT_MASK >> |
| DEV_DEFAULT_BOOT_SHIFT)) |
| value = VBNV_DEV_DEFAULT_BOOT_DISK; |
| |
| raw[DEV_FLAGS_OFFSET] &= ~DEV_DEFAULT_BOOT_MASK; |
| raw[DEV_FLAGS_OFFSET] |= (uint8_t)value << |
| DEV_DEFAULT_BOOT_SHIFT; |
| 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_DEV_BOOT_FASTBOOT_FULL_CAP: |
| if (value) |
| raw[DEV_FLAGS_OFFSET] |= |
| DEV_BOOT_FASTBOOT_FULL_CAP_MASK; |
| else |
| raw[DEV_FLAGS_OFFSET] &= |
| ~DEV_BOOT_FASTBOOT_FULL_CAP_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; |
| |
| case VBNV_TPM_REQUESTED_REBOOT: |
| if (value) |
| raw[TPM_FLAGS_OFFSET] |= TPM_REBOOTED; |
| else |
| raw[TPM_FLAGS_OFFSET] &= ~TPM_REBOOTED; |
| break; |
| |
| case VBNV_BACKUP_NVRAM_REQUEST: |
| if (value) |
| raw[BOOT_OFFSET] |= BOOT_BACKUP_NVRAM; |
| else |
| raw[BOOT_OFFSET] &= ~BOOT_BACKUP_NVRAM; |
| break; |
| |
| case VBNV_FW_TRY_NEXT: |
| if (value) |
| raw[BOOT2_OFFSET] |= BOOT2_TRY_NEXT; |
| else |
| raw[BOOT2_OFFSET] &= ~BOOT2_TRY_NEXT; |
| break; |
| |
| case VBNV_FW_TRIED: |
| if (value) |
| raw[BOOT2_OFFSET] |= BOOT2_TRIED; |
| else |
| raw[BOOT2_OFFSET] &= ~BOOT2_TRIED; |
| break; |
| |
| case VBNV_FW_RESULT: |
| /* Map out of range values to unknown */ |
| if (value > BOOT2_RESULT_MASK) |
| value = VBNV_FW_RESULT_UNKNOWN; |
| |
| raw[BOOT2_OFFSET] &= ~BOOT2_RESULT_MASK; |
| raw[BOOT2_OFFSET] |= (uint8_t)value; |
| break; |
| |
| case VBNV_FW_PREV_TRIED: |
| if (value) |
| raw[BOOT2_OFFSET] |= BOOT2_PREV_TRIED; |
| else |
| raw[BOOT2_OFFSET] &= ~BOOT2_PREV_TRIED; |
| break; |
| |
| case VBNV_FW_PREV_RESULT: |
| /* Map out of range values to unknown */ |
| if (value > BOOT2_RESULT_MASK) |
| value = VBNV_FW_RESULT_UNKNOWN; |
| |
| raw[BOOT2_OFFSET] &= ~BOOT2_PREV_RESULT_MASK; |
| raw[BOOT2_OFFSET] |= (uint8_t)value << BOOT2_PREV_RESULT_SHIFT; |
| break; |
| |
| case VBNV_FW_REQ_WIPEOUT: |
| if (value) |
| raw[HEADER_OFFSET] |= HEADER_WIPEOUT; |
| else |
| raw[HEADER_OFFSET] &= ~HEADER_WIPEOUT; |
| break; |
| |
| case VBNV_FASTBOOT_UNLOCK_IN_FW: |
| if (value) |
| raw[MISC_OFFSET] |= MISC_UNLOCK_FASTBOOT; |
| else |
| raw[MISC_OFFSET] &= ~MISC_UNLOCK_FASTBOOT; |
| break; |
| |
| case VBNV_BOOT_ON_AC_DETECT: |
| if (value) |
| raw[MISC_OFFSET] |= MISC_BOOT_ON_AC_DETECT; |
| else |
| raw[MISC_OFFSET] &= ~MISC_BOOT_ON_AC_DETECT; |
| break; |
| |
| case VBNV_TRY_RO_SYNC: |
| if (value) |
| raw[MISC_OFFSET] |= MISC_TRY_RO_SYNC; |
| else |
| raw[MISC_OFFSET] &= ~MISC_TRY_RO_SYNC; |
| break; |
| |
| case VBNV_BATTERY_CUTOFF_REQUEST: |
| if (value) |
| raw[MISC_OFFSET] |= MISC_BATTERY_CUTOFF_REQUEST; |
| else |
| raw[MISC_OFFSET] &= ~MISC_BATTERY_CUTOFF_REQUEST; |
| break; |
| |
| case VBNV_KERNEL_MAX_ROLLFORWARD: |
| raw[KERNEL_MAX_ROLLFORWARD1_OFFSET] = (uint8_t)(value); |
| raw[KERNEL_MAX_ROLLFORWARD2_OFFSET] = (uint8_t)(value >> 8); |
| raw[KERNEL_MAX_ROLLFORWARD3_OFFSET] = (uint8_t)(value >> 16); |
| raw[KERNEL_MAX_ROLLFORWARD4_OFFSET] = (uint8_t)(value >> 24); |
| break; |
| |
| default: |
| return 1; |
| } |
| |
| /* Need to regenerate CRC, since the value changed. */ |
| context->regenerate_crc = 1; |
| return 0; |
| } |