vboot_nvstorage.c revision 17b8224ea582b2ba90b30a3e8e2d913e49c7818a
1/* Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
2 * Use of this source code is governed by a BSD-style license that can be
3 * found in the LICENSE file.
4 */
5
6/* Non-volatile storage routines.
7 */
8
9#include "crc8.h"
10#include "utility.h"
11#include "vboot_common.h"
12#include "vboot_nvstorage.h"
13
14/* Constants for NV storage.  We use this rather than structs and
15 * bitfields so the data format is consistent across platforms and
16 * compilers. */
17#define HEADER_OFFSET                0
18#define HEADER_MASK                     0xC0
19#define HEADER_SIGNATURE                0x40
20#define HEADER_FIRMWARE_SETTINGS_RESET  0x20
21#define HEADER_KERNEL_SETTINGS_RESET    0x10
22
23#define BOOT_OFFSET                  1
24#define BOOT_DEBUG_RESET_MODE           0x80
25#define BOOT_DISABLE_DEV_REQUEST        0x40
26#define BOOT_OPROM_NEEDED               0x20
27#define BOOT_TRY_B_COUNT_MASK           0x0F
28
29#define RECOVERY_OFFSET              2
30#define LOCALIZATION_OFFSET          3
31
32#define DEV_FLAGS_OFFSET             4
33#define DEV_BOOT_USB_MASK               0x01
34#define DEV_BOOT_SIGNED_ONLY_MASK       0x02
35
36#define KERNEL_FIELD_OFFSET         11
37#define CRC_OFFSET                  15
38
39
40int VbNvSetup(VbNvContext* context) {
41  uint8_t* raw = context->raw;
42
43  /* Nothing has changed yet. */
44  context->raw_changed = 0;
45  context->regenerate_crc = 0;
46
47  /* Check data for consistency */
48  if ((HEADER_SIGNATURE != (raw[HEADER_OFFSET] & HEADER_MASK))
49      || (Crc8(raw, CRC_OFFSET) != raw[CRC_OFFSET])) {
50
51    /* Data is inconsistent (bad CRC or header), so reset defaults */
52    Memset(raw, 0, VBNV_BLOCK_SIZE);
53    raw[HEADER_OFFSET] = (HEADER_SIGNATURE | HEADER_FIRMWARE_SETTINGS_RESET |
54                          HEADER_KERNEL_SETTINGS_RESET);
55
56    /* Regenerate CRC on exit */
57    context->regenerate_crc = 1;
58  }
59
60  return 0;
61}
62
63
64int VbNvTeardown(VbNvContext* context) {
65
66  if (context->regenerate_crc) {
67    context->raw[CRC_OFFSET] = Crc8(context->raw, CRC_OFFSET);
68    context->regenerate_crc = 0;
69    context->raw_changed = 1;
70  }
71
72  return 0;
73}
74
75
76int VbNvGet(VbNvContext* context, VbNvParam param, uint32_t* dest) {
77  const uint8_t* raw = context->raw;
78
79  switch (param) {
80    case VBNV_FIRMWARE_SETTINGS_RESET:
81      *dest = (raw[HEADER_OFFSET] & HEADER_FIRMWARE_SETTINGS_RESET ? 1 : 0);
82      return 0;
83
84    case VBNV_KERNEL_SETTINGS_RESET:
85      *dest = (raw[HEADER_OFFSET] & HEADER_KERNEL_SETTINGS_RESET ? 1 : 0);
86      return 0;
87
88    case VBNV_DEBUG_RESET_MODE:
89      *dest = (raw[BOOT_OFFSET] & BOOT_DEBUG_RESET_MODE ? 1 : 0);
90      return 0;
91
92    case VBNV_TRY_B_COUNT:
93      *dest = raw[BOOT_OFFSET] & BOOT_TRY_B_COUNT_MASK;
94      return 0;
95
96    case VBNV_RECOVERY_REQUEST:
97      *dest = raw[RECOVERY_OFFSET];
98      return 0;
99
100    case VBNV_LOCALIZATION_INDEX:
101      *dest = raw[LOCALIZATION_OFFSET];
102      return 0;
103
104    case VBNV_KERNEL_FIELD:
105      *dest = (raw[KERNEL_FIELD_OFFSET]
106               | (raw[KERNEL_FIELD_OFFSET + 1] << 8)
107               | (raw[KERNEL_FIELD_OFFSET + 2] << 16)
108               | (raw[KERNEL_FIELD_OFFSET + 3] << 24));
109      return 0;
110
111    case VBNV_DEV_BOOT_USB:
112      *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_USB_MASK ? 1 : 0);
113      return 0;
114
115    case VBNV_DEV_BOOT_SIGNED_ONLY:
116      *dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_SIGNED_ONLY_MASK ? 1 : 0);
117      return 0;
118
119    case VBNV_DISABLE_DEV_REQUEST:
120      *dest = (raw[BOOT_OFFSET] & BOOT_DISABLE_DEV_REQUEST ? 1 : 0);
121      return 0;
122
123    case VBNV_OPROM_NEEDED:
124      *dest = (raw[BOOT_OFFSET] & BOOT_OPROM_NEEDED ? 1 : 0);
125      return 0;
126
127    default:
128      return 1;
129  }
130}
131
132
133int VbNvSet(VbNvContext* context, VbNvParam param, uint32_t value) {
134  uint8_t* raw = context->raw;
135  uint32_t current;
136
137  /* If we're not changing the value, we don't need to regenerate the CRC. */
138  if (0 == VbNvGet(context, param, &current) && current == value)
139    return 0;
140
141  switch (param) {
142    case VBNV_FIRMWARE_SETTINGS_RESET:
143      if (value)
144        raw[HEADER_OFFSET] |= HEADER_FIRMWARE_SETTINGS_RESET;
145      else
146        raw[HEADER_OFFSET] &= ~HEADER_FIRMWARE_SETTINGS_RESET;
147      break;
148
149    case VBNV_KERNEL_SETTINGS_RESET:
150      if (value)
151        raw[HEADER_OFFSET] |= HEADER_KERNEL_SETTINGS_RESET;
152      else
153        raw[HEADER_OFFSET] &= ~HEADER_KERNEL_SETTINGS_RESET;
154      break;
155
156    case VBNV_DEBUG_RESET_MODE:
157      if (value)
158        raw[BOOT_OFFSET] |= BOOT_DEBUG_RESET_MODE;
159      else
160        raw[BOOT_OFFSET] &= ~BOOT_DEBUG_RESET_MODE;
161      break;
162
163    case VBNV_TRY_B_COUNT:
164      /* Clip to valid range. */
165      if (value > BOOT_TRY_B_COUNT_MASK)
166        value = BOOT_TRY_B_COUNT_MASK;
167
168      raw[BOOT_OFFSET] &= ~BOOT_TRY_B_COUNT_MASK;
169      raw[BOOT_OFFSET] |= (uint8_t)value;
170      break;
171
172    case VBNV_RECOVERY_REQUEST:
173      /* Map values outside the valid range to the legacy reason, since we
174       * can't determine if we're called from kernel or user mode. */
175      if (value > 0xFF)
176        value = VBNV_RECOVERY_LEGACY;
177      raw[RECOVERY_OFFSET] = (uint8_t)value;
178      break;
179
180    case VBNV_LOCALIZATION_INDEX:
181      /* Map values outside the valid range to the default index. */
182      if (value > 0xFF)
183        value = 0;
184      raw[LOCALIZATION_OFFSET] = (uint8_t)value;
185      break;
186
187    case VBNV_KERNEL_FIELD:
188      raw[KERNEL_FIELD_OFFSET] = (uint8_t)(value);
189      raw[KERNEL_FIELD_OFFSET + 1] = (uint8_t)(value >> 8);
190      raw[KERNEL_FIELD_OFFSET + 2] = (uint8_t)(value >> 16);
191      raw[KERNEL_FIELD_OFFSET + 3] = (uint8_t)(value >> 24);
192      break;
193
194    case VBNV_DEV_BOOT_USB:
195      if (value)
196        raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_USB_MASK;
197      else
198        raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_USB_MASK;
199      break;
200
201    case VBNV_DEV_BOOT_SIGNED_ONLY:
202      if (value)
203        raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_SIGNED_ONLY_MASK;
204      else
205        raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_SIGNED_ONLY_MASK;
206      break;
207
208    case VBNV_DISABLE_DEV_REQUEST:
209      if (value)
210        raw[BOOT_OFFSET] |= BOOT_DISABLE_DEV_REQUEST;
211      else
212        raw[BOOT_OFFSET] &= ~BOOT_DISABLE_DEV_REQUEST;
213      break;
214
215    case VBNV_OPROM_NEEDED:
216      if (value)
217        raw[BOOT_OFFSET] |= BOOT_OPROM_NEEDED;
218      else
219        raw[BOOT_OFFSET] &= ~BOOT_OPROM_NEEDED;
220      break;
221
222    default:
223      return 1;
224  }
225
226  /* Need to regenerate CRC, since the value changed. */
227  context->regenerate_crc = 1;
228  return 0;
229}
230