1/* Copyright (c) 2013 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#include "sysincludes.h"
9
10#include "crc8.h"
11#include "utility.h"
12#include "vboot_common.h"
13#include "vboot_nvstorage.h"
14
15/*
16 * Constants for NV storage.  We use this rather than structs and bitfields so
17 * the data format is consistent across platforms and compilers.
18 *
19 * These constants must match the equivalent constants in 2lib/2nvstorage.c.
20 * (We currently don't share a common header file because we're tring to keep
21 * the two libs independent, and we hope to deprecate this one.)
22 */
23#define HEADER_OFFSET                0
24#define HEADER_MASK                     0xC0
25#define HEADER_SIGNATURE                0x40
26#define HEADER_FIRMWARE_SETTINGS_RESET  0x20
27#define HEADER_KERNEL_SETTINGS_RESET    0x10
28
29#define BOOT_OFFSET                  1
30#define BOOT_DEBUG_RESET_MODE           0x80
31#define BOOT_DISABLE_DEV_REQUEST        0x40
32#define BOOT_OPROM_NEEDED               0x20
33#define BOOT_BACKUP_NVRAM               0x10
34#define BOOT_TRY_B_COUNT_MASK           0x0F
35
36#define RECOVERY_OFFSET              2
37#define LOCALIZATION_OFFSET          3
38
39#define DEV_FLAGS_OFFSET             4
40#define DEV_BOOT_USB_MASK               0x01
41#define DEV_BOOT_SIGNED_ONLY_MASK       0x02
42#define DEV_BOOT_LEGACY_MASK            0x04
43
44#define TPM_FLAGS_OFFSET             5
45#define TPM_CLEAR_OWNER_REQUEST         0x01
46#define TPM_CLEAR_OWNER_DONE            0x02
47
48#define RECOVERY_SUBCODE_OFFSET      6
49
50#define BOOT2_OFFSET                 7
51#define BOOT2_RESULT_MASK               0x03
52#define BOOT2_TRIED                     0x04
53#define BOOT2_TRY_NEXT                  0x08
54#define BOOT2_PREV_RESULT_MASK          0x30
55#define BOOT2_PREV_RESULT_SHIFT 4  /* Number of bits to shift result */
56#define BOOT2_PREV_TRIED                0x40
57
58#define KERNEL_FIELD_OFFSET         11
59#define CRC_OFFSET                  15
60
61int VbNvSetup(VbNvContext *context)
62{
63	uint8_t *raw = context->raw;
64
65	/* Nothing has changed yet. */
66	context->raw_changed = 0;
67	context->regenerate_crc = 0;
68
69	/* Check data for consistency */
70	if ((HEADER_SIGNATURE != (raw[HEADER_OFFSET] & HEADER_MASK))
71	    || (Crc8(raw, CRC_OFFSET) != raw[CRC_OFFSET])) {
72		/* Data is inconsistent (bad CRC or header); reset defaults */
73		Memset(raw, 0, VBNV_BLOCK_SIZE);
74		raw[HEADER_OFFSET] = (HEADER_SIGNATURE |
75				      HEADER_FIRMWARE_SETTINGS_RESET |
76				      HEADER_KERNEL_SETTINGS_RESET);
77
78		/* Regenerate CRC on exit */
79		context->regenerate_crc = 1;
80	}
81
82	return 0;
83}
84
85int VbNvTeardown(VbNvContext *context)
86{
87	if (context->regenerate_crc) {
88		context->raw[CRC_OFFSET] = Crc8(context->raw, CRC_OFFSET);
89		context->regenerate_crc = 0;
90		context->raw_changed = 1;
91	}
92
93	return 0;
94}
95
96int VbNvGet(VbNvContext *context, VbNvParam param, uint32_t *dest)
97{
98	const uint8_t *raw = context->raw;
99
100	switch (param) {
101	case VBNV_FIRMWARE_SETTINGS_RESET:
102		*dest = (raw[HEADER_OFFSET] & HEADER_FIRMWARE_SETTINGS_RESET ?
103			 1 : 0);
104		return 0;
105
106	case VBNV_KERNEL_SETTINGS_RESET:
107		*dest = (raw[HEADER_OFFSET] & HEADER_KERNEL_SETTINGS_RESET ?
108			 1 : 0);
109		return 0;
110
111	case VBNV_DEBUG_RESET_MODE:
112		*dest = (raw[BOOT_OFFSET] & BOOT_DEBUG_RESET_MODE ? 1 : 0);
113		return 0;
114
115	case VBNV_TRY_B_COUNT:
116	case VBNV_FW_TRY_COUNT:
117		*dest = raw[BOOT_OFFSET] & BOOT_TRY_B_COUNT_MASK;
118		return 0;
119
120	case VBNV_RECOVERY_REQUEST:
121		*dest = raw[RECOVERY_OFFSET];
122		return 0;
123
124	case VBNV_RECOVERY_SUBCODE:
125		*dest = raw[RECOVERY_SUBCODE_OFFSET];
126		return 0;
127
128	case VBNV_LOCALIZATION_INDEX:
129		*dest = raw[LOCALIZATION_OFFSET];
130		return 0;
131
132	case VBNV_KERNEL_FIELD:
133		*dest = (raw[KERNEL_FIELD_OFFSET]
134			 | (raw[KERNEL_FIELD_OFFSET + 1] << 8)
135			 | (raw[KERNEL_FIELD_OFFSET + 2] << 16)
136			 | (raw[KERNEL_FIELD_OFFSET + 3] << 24));
137		return 0;
138
139	case VBNV_DEV_BOOT_USB:
140		*dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_USB_MASK ? 1 : 0);
141		return 0;
142
143	case VBNV_DEV_BOOT_LEGACY:
144		*dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_LEGACY_MASK ? 1 : 0);
145		return 0;
146
147	case VBNV_DEV_BOOT_SIGNED_ONLY:
148		*dest = (raw[DEV_FLAGS_OFFSET] & DEV_BOOT_SIGNED_ONLY_MASK ?
149			 1 : 0);
150		return 0;
151
152	case VBNV_DISABLE_DEV_REQUEST:
153		*dest = (raw[BOOT_OFFSET] & BOOT_DISABLE_DEV_REQUEST ? 1 : 0);
154		return 0;
155
156	case VBNV_OPROM_NEEDED:
157		*dest = (raw[BOOT_OFFSET] & BOOT_OPROM_NEEDED ? 1 : 0);
158		return 0;
159
160	case VBNV_CLEAR_TPM_OWNER_REQUEST:
161		*dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_REQUEST ?
162			 1 : 0);
163		return 0;
164
165	case VBNV_CLEAR_TPM_OWNER_DONE:
166		*dest = (raw[TPM_FLAGS_OFFSET] & TPM_CLEAR_OWNER_DONE ? 1 : 0);
167		return 0;
168
169	case VBNV_BACKUP_NVRAM_REQUEST:
170		*dest = (raw[BOOT_OFFSET] & BOOT_BACKUP_NVRAM ? 1 : 0);
171		return 0;
172
173	case VBNV_FW_TRY_NEXT:
174		*dest = (raw[BOOT2_OFFSET] & BOOT2_TRY_NEXT ? 1 : 0);
175		return 0;
176
177	case VBNV_FW_TRIED:
178		*dest = (raw[BOOT2_OFFSET] & BOOT2_TRIED ? 1 : 0);
179		return 0;
180
181	case VBNV_FW_RESULT:
182		*dest = raw[BOOT2_OFFSET] & BOOT2_RESULT_MASK;
183		return 0;
184
185	case VBNV_FW_PREV_TRIED:
186		*dest = (raw[BOOT2_OFFSET] & BOOT2_PREV_TRIED ? 1 : 0);
187		return 0;
188
189	case VBNV_FW_PREV_RESULT:
190		*dest = (raw[BOOT2_OFFSET] & BOOT2_PREV_RESULT_MASK)
191			>> BOOT2_PREV_RESULT_SHIFT;
192		return 0;
193
194	default:
195		return 1;
196	}
197}
198
199int VbNvSet(VbNvContext *context, VbNvParam param, uint32_t value)
200{
201	uint8_t *raw = context->raw;
202	uint32_t current;
203
204	/* If not changing the value, don't regenerate the CRC. */
205	if (0 == VbNvGet(context, param, &current) && current == value)
206		return 0;
207
208	switch (param) {
209	case VBNV_FIRMWARE_SETTINGS_RESET:
210		if (value)
211			raw[HEADER_OFFSET] |= HEADER_FIRMWARE_SETTINGS_RESET;
212		else
213			raw[HEADER_OFFSET] &= ~HEADER_FIRMWARE_SETTINGS_RESET;
214		break;
215
216	case VBNV_KERNEL_SETTINGS_RESET:
217		if (value)
218			raw[HEADER_OFFSET] |= HEADER_KERNEL_SETTINGS_RESET;
219		else
220			raw[HEADER_OFFSET] &= ~HEADER_KERNEL_SETTINGS_RESET;
221		break;
222
223	case VBNV_DEBUG_RESET_MODE:
224		if (value)
225			raw[BOOT_OFFSET] |= BOOT_DEBUG_RESET_MODE;
226		else
227			raw[BOOT_OFFSET] &= ~BOOT_DEBUG_RESET_MODE;
228		break;
229
230	case VBNV_TRY_B_COUNT:
231	case VBNV_FW_TRY_COUNT:
232		/* Clip to valid range. */
233		if (value > BOOT_TRY_B_COUNT_MASK)
234			value = BOOT_TRY_B_COUNT_MASK;
235
236		raw[BOOT_OFFSET] &= ~BOOT_TRY_B_COUNT_MASK;
237		raw[BOOT_OFFSET] |= (uint8_t)value;
238		break;
239
240	case VBNV_RECOVERY_REQUEST:
241		/*
242		 * Map values outside the valid range to the legacy reason,
243		 * since we can't determine if we're called from kernel or user
244		 * mode.
245		 */
246		if (value > 0xFF)
247			value = VBNV_RECOVERY_LEGACY;
248		raw[RECOVERY_OFFSET] = (uint8_t)value;
249		break;
250
251	case VBNV_RECOVERY_SUBCODE:
252		raw[RECOVERY_SUBCODE_OFFSET] = (uint8_t)value;
253		break;
254
255	case VBNV_LOCALIZATION_INDEX:
256		/* Map values outside the valid range to the default index. */
257		if (value > 0xFF)
258			value = 0;
259		raw[LOCALIZATION_OFFSET] = (uint8_t)value;
260		break;
261
262	case VBNV_KERNEL_FIELD:
263		raw[KERNEL_FIELD_OFFSET] = (uint8_t)(value);
264		raw[KERNEL_FIELD_OFFSET + 1] = (uint8_t)(value >> 8);
265		raw[KERNEL_FIELD_OFFSET + 2] = (uint8_t)(value >> 16);
266		raw[KERNEL_FIELD_OFFSET + 3] = (uint8_t)(value >> 24);
267		break;
268
269	case VBNV_DEV_BOOT_USB:
270		if (value)
271			raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_USB_MASK;
272		else
273			raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_USB_MASK;
274		break;
275
276	case VBNV_DEV_BOOT_LEGACY:
277		if (value)
278			raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_LEGACY_MASK;
279		else
280			raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_LEGACY_MASK;
281		break;
282
283	case VBNV_DEV_BOOT_SIGNED_ONLY:
284		if (value)
285			raw[DEV_FLAGS_OFFSET] |= DEV_BOOT_SIGNED_ONLY_MASK;
286		else
287			raw[DEV_FLAGS_OFFSET] &= ~DEV_BOOT_SIGNED_ONLY_MASK;
288		break;
289
290	case VBNV_DISABLE_DEV_REQUEST:
291		if (value)
292			raw[BOOT_OFFSET] |= BOOT_DISABLE_DEV_REQUEST;
293		else
294			raw[BOOT_OFFSET] &= ~BOOT_DISABLE_DEV_REQUEST;
295		break;
296
297	case VBNV_OPROM_NEEDED:
298		if (value)
299			raw[BOOT_OFFSET] |= BOOT_OPROM_NEEDED;
300		else
301			raw[BOOT_OFFSET] &= ~BOOT_OPROM_NEEDED;
302		break;
303
304	case VBNV_CLEAR_TPM_OWNER_REQUEST:
305		if (value)
306			raw[TPM_FLAGS_OFFSET] |= TPM_CLEAR_OWNER_REQUEST;
307		else
308			raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_REQUEST;
309		break;
310
311	case VBNV_CLEAR_TPM_OWNER_DONE:
312		if (value)
313			raw[TPM_FLAGS_OFFSET] |= TPM_CLEAR_OWNER_DONE;
314		else
315			raw[TPM_FLAGS_OFFSET] &= ~TPM_CLEAR_OWNER_DONE;
316		break;
317
318	case VBNV_BACKUP_NVRAM_REQUEST:
319		if (value)
320			raw[BOOT_OFFSET] |= BOOT_BACKUP_NVRAM;
321		else
322			raw[BOOT_OFFSET] &= ~BOOT_BACKUP_NVRAM;
323		break;
324
325	case VBNV_FW_TRY_NEXT:
326		if (value)
327			raw[BOOT2_OFFSET] |= BOOT2_TRY_NEXT;
328		else
329			raw[BOOT2_OFFSET] &= ~BOOT2_TRY_NEXT;
330		break;
331
332	case VBNV_FW_TRIED:
333		if (value)
334			raw[BOOT2_OFFSET] |= BOOT2_TRIED;
335		else
336			raw[BOOT2_OFFSET] &= ~BOOT2_TRIED;
337		break;
338
339	case VBNV_FW_RESULT:
340		/* Map out of range values to unknown */
341		if (value > BOOT2_RESULT_MASK)
342			value = VBNV_FW_RESULT_UNKNOWN;
343
344		raw[BOOT2_OFFSET] &= ~BOOT2_RESULT_MASK;
345		raw[BOOT2_OFFSET] |= (uint8_t)value;
346		break;
347
348	case VBNV_FW_PREV_TRIED:
349		if (value)
350			raw[BOOT2_OFFSET] |= BOOT2_PREV_TRIED;
351		else
352			raw[BOOT2_OFFSET] &= ~BOOT2_PREV_TRIED;
353		break;
354
355	case VBNV_FW_PREV_RESULT:
356		/* Map out of range values to unknown */
357		if (value > BOOT2_RESULT_MASK)
358			value = VBNV_FW_RESULT_UNKNOWN;
359
360		raw[BOOT2_OFFSET] &= ~BOOT2_PREV_RESULT_MASK;
361		raw[BOOT2_OFFSET] |= (uint8_t)value << BOOT2_PREV_RESULT_SHIFT;
362		break;
363
364	default:
365		return 1;
366	}
367
368	/* Need to regenerate CRC, since the value changed. */
369	context->regenerate_crc = 1;
370	return 0;
371}
372