xref: /aosp_15_r20/external/vboot_reference/host/lib/crossystem.c (revision 8617a60d3594060b7ecbd21bc622a7c14f3cf2bc)
1 /* Copyright 2012 The ChromiumOS Authors
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 #include <ctype.h>
7 #include <fcntl.h>
8 #include <stddef.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <sys/file.h>
12 #include <unistd.h>
13 
14 #include "2api.h"
15 #include "2common.h"
16 #include "2nvstorage.h"
17 #include "2sysincludes.h"
18 #include "chromeos_config.h"
19 #include "crossystem_arch.h"
20 #include "crossystem.h"
21 #include "crossystem_vbnv.h"
22 #include "host_common.h"
23 #include "flashrom.h"
24 #include "subprocess.h"
25 #include "vboot_struct.h"
26 
27 /* Filename for crossystem lock */
28 #define CROSSYSTEM_LOCK_PATH (CROSSYSTEM_LOCK_DIR "/crossystem.lock")
29 
30 /* Filename for kernel command line */
31 #define KERNEL_CMDLINE_PATH "/proc/cmdline"
32 
33 /* Filename for the tpm_clear_request executable. */
34 #define TPM_CLEAR_REQUEST_EXEC_NAME "/usr/sbin/tpm_clear_request"
35 
36 /* Fields that GetVdatString() can get */
37 typedef enum VdatStringField {
38 	VDAT_STRING_DEPRECATED_TIMERS = 0,  /* Timer values */
39 	VDAT_STRING_LOAD_FIRMWARE_DEBUG,  /* LoadFirmware() debug info */
40 	VDAT_STRING_DEPRECATED_LOAD_KERNEL_DEBUG,  /* vb2api_load_kernel()
41 						      debug info */
42 	VDAT_STRING_MAINFW_ACT  /* Active main firmware */
43 } VdatStringField;
44 
45 
46 /* Fields that GetVdatInt() can get */
47 typedef enum VdatIntField {
48 	VDAT_INT_FLAGS = 0,           /* Flags */
49 	VDAT_INT_HEADER_VERSION,      /* Header version for VbSharedData */
50 	VDAT_INT_DEVSW_BOOT,          /* Dev switch position at boot */
51 	VDAT_INT_RECSW_BOOT,          /* Recovery switch position at boot */
52 	VDAT_INT_HW_WPSW_BOOT,        /* Hardware WP switch position at boot */
53 
54 	VDAT_INT_FW_VERSION_TPM,      /* Current firmware version in TPM */
55 	VDAT_INT_KERNEL_VERSION_TPM,  /* Current kernel version in TPM */
56 	VDAT_INT_KERNEL_KEY_VERIFIED, /* Kernel key verified using
57 				       * signature, not just hash */
58 	VDAT_INT_RECOVERY_REASON,     /* Recovery reason for current boot */
59 	VDAT_INT_FW_BOOT2,            /* Firmware selection by vboot2 */
60 	VDAT_INT_FW_VERSION_ACT,      /* Current active firmware version */
61 	VDAT_INT_KERNEL_VERSION_ACT,  /* Current active kernel version */
62 } VdatIntField;
63 
64 
65 /* Description of build options that may be specified on the
66  * kernel command line. */
67 typedef enum VbBuildOption {
68 	VB_BUILD_OPTION_UNKNOWN,
69 	VB_BUILD_OPTION_DEBUG,
70 	VB_BUILD_OPTION_NODEBUG
71 } VbBuildOption;
72 
73 static const char *fw_results[] = {"unknown", "trying", "success", "failure"};
74 static const char *default_boot[] = {"disk", "usb", "altfw"};
75 
76 /* Masks for kern_nv usage by kernel. */
77 #define KERN_NV_FWUPDATE_TRIES_MASK 0x000F
78 #define KERN_NV_BLOCK_DEVMODE_FLAG  0x0010
79 #define KERN_NV_TPM_ATTACK_FLAG     0x0020
80 /* If you want to use the remaining currently-unused bits in kern_nv
81  * for something kernel-y, define a new field (the way we did for
82  * fwupdate_tries).  Don't just modify kern_nv directly, because that
83  * makes it too easy to accidentally corrupt other sub-fields. */
84 #define KERN_NV_CURRENTLY_UNUSED    0xFFC0
85 
86 /* Return true if the FWID starts with the specified string. */
FwidStartsWith(const char * start)87 int FwidStartsWith(const char *start)
88 {
89 	char fwid[VB_MAX_STRING_PROPERTY];
90 	if (VbGetSystemPropertyString("fwid", fwid, sizeof(fwid)) != 0)
91 		return 0;
92 
93 	return 0 == strncmp(fwid, start, strlen(start));
94 }
95 
96 /* Acquire the lock for crossystem SetSystemProperty call. */
AcquireCrossystemLock(void)97 static int AcquireCrossystemLock(void)
98 {
99 	int lock_fd;
100 
101 	lock_fd = open(CROSSYSTEM_LOCK_PATH, O_RDWR | O_CREAT, 0600);
102 	if (lock_fd < 0)
103 		return -1;
104 
105 	if (flock(lock_fd, LOCK_EX) < 0)
106 		return -1;
107 
108 	return lock_fd;
109 }
110 
111 /* Release the lock for crossystem SetSystemProperty call. */
ReleaseCrossystemLock(int lock_fd)112 static int ReleaseCrossystemLock(int lock_fd)
113 {
114 	if (flock(lock_fd, F_UNLCK) < 0)
115 		return -1;
116 
117 	close(lock_fd);
118 
119 	return 0;
120 }
121 
122 /* Check if system FW type is equivalent to a given name */
CheckFwType(const char * name)123 static bool CheckFwType(const char *name)
124 {
125 	char fwtype_buf[VB_MAX_STRING_PROPERTY];
126 	int fwtype_ret;
127 
128 	fwtype_ret = VbGetSystemPropertyString("mainfw_type",
129 		fwtype_buf, sizeof(fwtype_buf));
130 
131 	if (fwtype_ret == 0 && !strcasecmp(fwtype_buf, name))
132 		return true;
133 
134 	return false;
135 }
136 
get_fake_context(void)137 static struct vb2_context *get_fake_context(void)
138 {
139 	static uint8_t fake_workbuf[sizeof(struct vb2_shared_data) + 16]
140 		__attribute__((aligned(VB2_WORKBUF_ALIGN)));
141 	static struct vb2_context *fake_ctx;
142 
143 	if (fake_ctx)
144 		return fake_ctx;
145 
146 	vb2api_init(fake_workbuf, sizeof(fake_workbuf), &fake_ctx);
147 
148 	return fake_ctx;
149 }
150 
151 static int vnc_read;
152 
vb2_get_nv_storage(enum vb2_nv_param param)153 int vb2_get_nv_storage(enum vb2_nv_param param)
154 {
155 	VbSharedDataHeader* sh = VbSharedDataRead();
156 	struct vb2_context *ctx = get_fake_context();
157 
158 	if (!sh)
159 		return -1;
160 
161 	/* TODO: locking around NV access */
162 	if (!vnc_read) {
163 		if (sh && sh->flags & VBSD_NVDATA_V2)
164 			ctx->flags |= VB2_CONTEXT_NVDATA_V2;
165 		if (0 != vb2_read_nv_storage(ctx)) {
166 			free(sh);
167 			return -1;
168 		}
169 		vb2_nv_init(ctx);
170 
171 		/* TODO: If vnc.raw_changed, attempt to reopen NVRAM for write
172 		 * and save the new defaults.  If we're able to, log. */
173 
174 		vnc_read = 1;
175 	}
176 
177 	free(sh);
178 	return (int)vb2_nv_get(ctx, param);
179 }
180 
vb2_set_nv_storage(enum vb2_nv_param param,int value)181 int vb2_set_nv_storage(enum vb2_nv_param param, int value)
182 {
183 	VbSharedDataHeader* sh = VbSharedDataRead();
184 	struct vb2_context *ctx = get_fake_context();
185 
186 	if (!sh)
187 		return -1;
188 
189 	/* TODO: locking around NV access */
190 	if (sh && sh->flags & VBSD_NVDATA_V2)
191 		ctx->flags |= VB2_CONTEXT_NVDATA_V2;
192 	if (0 != vb2_read_nv_storage(ctx)) {
193 		free(sh);
194 		return -1;
195 	}
196 	vb2_nv_init(ctx);
197 	vb2_nv_set(ctx, param, (uint32_t)value);
198 
199 	if (ctx->flags & VB2_CONTEXT_NVDATA_CHANGED) {
200 		vnc_read = 0;
201 		if (0 != vb2_write_nv_storage(ctx)) {
202 			free(sh);
203 			return -1;
204 		}
205 		ctx->flags &= ~VB2_CONTEXT_NVDATA_CHANGED;
206 	}
207 
208 	/* Success */
209 	free(sh);
210 	return 0;
211 }
212 
213 /*
214  * Set a param value, and try to flag it for persistent backup.  It's okay if
215  * backup isn't supported (which it isn't, in current designs). It's
216  * best-effort only.
217  */
vb2_set_nv_storage_with_backup(enum vb2_nv_param param,int value)218 static int vb2_set_nv_storage_with_backup(enum vb2_nv_param param, int value)
219 {
220 	int retval;
221 	retval = vb2_set_nv_storage(param, value);
222 	if (!retval)
223 		vb2_set_nv_storage(VB2_NV_BACKUP_NVRAM_REQUEST, 1);
224 	return retval;
225 }
226 
227 /* Find what build/debug status is specified on the kernel command
228  * line, if any. */
VbScanBuildOption(void)229 static VbBuildOption VbScanBuildOption(void)
230 {
231 	FILE* f = NULL;
232 	char buf[4096] = "";
233 	char *t, *saveptr;
234 	const char *delimiters = " \r\n";
235 
236 	f = fopen(KERNEL_CMDLINE_PATH, "r");
237 	if (NULL != f) {
238 		if (NULL == fgets(buf, sizeof(buf), f))
239 			buf[0] = 0;
240 		fclose(f);
241 	}
242 	for (t = strtok_r(buf, delimiters, &saveptr); t;
243 	     t = strtok_r(NULL, delimiters, &saveptr)) {
244 		if (0 == strcmp(t, "cros_debug"))
245 			return VB_BUILD_OPTION_DEBUG;
246 		else if (0 == strcmp(t, "cros_nodebug"))
247 			return VB_BUILD_OPTION_NODEBUG;
248 	}
249 
250 	return VB_BUILD_OPTION_UNKNOWN;
251 }
252 
253 /* Determine whether the running OS image was built for debugging.
254  * Returns 1 if yes, 0 if no or indeterminate. */
VbGetDebugBuild(void)255 static vb2_error_t VbGetDebugBuild(void)
256 {
257 	return VB_BUILD_OPTION_DEBUG == VbScanBuildOption();
258 }
259 
260 /* Determine whether OS-level debugging should be allowed.
261  * Returns 1 if yes, 0 if no or indeterminate. */
VbGetCrosDebug(void)262 static int VbGetCrosDebug(void)
263 {
264 	/* If the currently running system specifies its debug status, use
265 	 * that in preference to other indicators. */
266 	VbBuildOption option = VbScanBuildOption();
267 	if (VB_BUILD_OPTION_DEBUG == option) {
268 		return 1;
269 	} else if (VB_BUILD_OPTION_NODEBUG == option) {
270 		return 0;
271 	}
272 
273 	/* Command line is silent; allow debug if the dev switch is on. */
274 	if (1 == VbGetSystemPropertyInt("devsw_boot"))
275 		return 1;
276 
277 	/* All other cases disallow debug. */
278 	return 0;
279 }
280 
GetVdatLoadFirmwareDebug(char * dest,int size,const VbSharedDataHeader * sh)281 static int GetVdatLoadFirmwareDebug(char *dest, int size,
282 				    const VbSharedDataHeader *sh)
283 {
284 	snprintf(dest, size,
285 		 "Check A result=%d\n"
286 		 "Check B result=%d\n"
287 		 "Firmware index booted=0x%02x\n"
288 		 "Active firmware version=0x%08x\n"
289 		 "Firmware version in TPM =0x%08x\n"
290 		 "Lowest combined version from firmware=0x%08x\n",
291 		 sh->check_fw_a_result,
292 		 sh->check_fw_b_result,
293 		 sh->firmware_index,
294 		 sh->fw_version_act,
295 		 sh->fw_version_tpm,
296 		 sh->fw_version_lowest);
297 	return 0;
298 }
299 
GetVdatString(char * dest,int size,VdatStringField field)300 static int GetVdatString(char *dest, int size, VdatStringField field)
301 {
302 	VbSharedDataHeader *sh = VbSharedDataRead();
303 	int value = 0;
304 
305 	if (!sh)
306 		return -1;
307 
308 	switch (field) {
309 		case VDAT_STRING_LOAD_FIRMWARE_DEBUG:
310 			value = GetVdatLoadFirmwareDebug(dest, size, sh);
311 			break;
312 
313 		case VDAT_STRING_MAINFW_ACT:
314 			switch(sh->firmware_index) {
315 				case 0:
316 					StrCopy(dest, "A", size);
317 					break;
318 				case 1:
319 					StrCopy(dest, "B", size);
320 					break;
321 				case 0xFF:
322 					StrCopy(dest, "recovery", size);
323 					break;
324 				default:
325 					value = -1;
326 			}
327 			break;
328 
329 		default:
330 			value = -1;
331 			break;
332 	}
333 
334 	free(sh);
335 	return value;
336 }
337 
FwidMajorVersion(void)338 static int FwidMajorVersion(void)
339 {
340 	char fwid[VB_MAX_STRING_PROPERTY];
341 	int version;
342 
343 	if (VbGetSystemPropertyString("fwid", fwid, sizeof(fwid)) != 0)
344 		return -1;
345 
346 	if (sscanf(fwid, "%*[^.].%d", &version) != 1 || version <= 0) {
347 		fprintf(stderr, "WARNING: Cannot parse major version from %s\n",
348 			fwid);
349 		return -1;
350 	}
351 
352 	return version;
353 }
354 
GetVdatInt(VdatIntField field)355 static int GetVdatInt(VdatIntField field)
356 {
357 	VbSharedDataHeader* sh = VbSharedDataRead();
358 	int value = -1;
359 
360 	if (!sh)
361 		return -1;
362 
363 	/* Fields supported in version 1 */
364 	switch (field) {
365 		case VDAT_INT_FLAGS:
366 			value = (int)sh->flags;
367 			break;
368 		case VDAT_INT_HEADER_VERSION:
369 			value = sh->struct_version;
370 			break;
371 		case VDAT_INT_KERNEL_KEY_VERIFIED:
372 			value = (sh->flags & VBSD_KERNEL_KEY_VERIFIED ? 1 : 0);
373 			break;
374 		case VDAT_INT_FW_VERSION_TPM:
375 			/* b/269204332#comment5: Before CL:2054270 and CL:2056343,
376 			   fw_version_tpm was always 0. */
377 			if (sh->struct_version <= 2 && FwidMajorVersion() < 12935)
378 				value = (int)sh->fw_version_act;
379 			else
380 				value = (int)sh->fw_version_tpm;
381 			break;
382 		case VDAT_INT_KERNEL_VERSION_TPM:
383 			value = (int)sh->kernel_version_tpm;
384 			break;
385 		case VDAT_INT_FW_BOOT2:
386 			value = (sh->flags & VBSD_BOOT_FIRMWARE_VBOOT2 ? 1 : 0);
387 			break;
388 		case VDAT_INT_FW_VERSION_ACT:
389 			value = (int)sh->fw_version_act;
390 			break;
391 		default:
392 			break;
393 	}
394 
395 	/* Fields added in struct version 2 */
396 	if (sh->struct_version >= 2) {
397 		switch(field) {
398 			case VDAT_INT_DEVSW_BOOT:
399 				value = (sh->flags &
400 					 VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0);
401 				break;
402 			case VDAT_INT_RECSW_BOOT:
403 				value = (sh->flags &
404 					 VBSD_BOOT_REC_SWITCH_ON ? 1 : 0);
405 				break;
406 			case VDAT_INT_HW_WPSW_BOOT:
407 				value = (sh->flags &
408 					 VBSD_BOOT_FIRMWARE_WP_ENABLED ? 1 : 0);
409 				break;
410 			case VDAT_INT_RECOVERY_REASON:
411 				value = sh->recovery_reason;
412 				break;
413 			default:
414 				break;
415 		}
416 	}
417 
418 	/* Fields added in struct version 3 */
419 	if (sh->struct_version >= 3) {
420 		switch(field) {
421 			case VDAT_INT_KERNEL_VERSION_ACT:
422 				value = (int)sh->kernel_version_act;
423 				break;
424 			default:
425 				break;
426 		}
427 	}
428 
429 	free(sh);
430 	return value;
431 }
432 
433 /* Return version of VbSharedData struct or -1 if not found. */
VbSharedDataVersion(void)434 int VbSharedDataVersion(void)
435 {
436 	return GetVdatInt(VDAT_INT_HEADER_VERSION);
437 }
438 
VbGetSystemPropertyInt(const char * name)439 int VbGetSystemPropertyInt(const char *name)
440 {
441 	int value = -1;
442 
443 	/* Check architecture-dependent properties first */
444 	value = VbGetArchPropertyInt(name);
445 	if (-1 != value)
446 		return value;
447 
448 	/* NV storage values */
449 	else if (!strcasecmp(name,"kern_nv")) {
450 		value = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD);
451 	} else if (!strcasecmp(name,"nvram_cleared")) {
452 		value = vb2_get_nv_storage(VB2_NV_KERNEL_SETTINGS_RESET);
453 	} else if (!strcasecmp(name,"recovery_request")) {
454 		value = vb2_get_nv_storage(VB2_NV_RECOVERY_REQUEST);
455 	} else if (!strcasecmp(name,"diagnostic_request")) {
456 		value = vb2_get_nv_storage(VB2_NV_DIAG_REQUEST);
457 	} else if (!strcasecmp(name,"dbg_reset")) {
458 		value = vb2_get_nv_storage(VB2_NV_DEBUG_RESET_MODE);
459 	} else if (!strcasecmp(name,"disable_dev_request")) {
460 		value = vb2_get_nv_storage(VB2_NV_DISABLE_DEV_REQUEST);
461 	} else if (!strcasecmp(name,"clear_tpm_owner_request")) {
462 		if (EXTERNAL_TPM_CLEAR_REQUEST && CheckFwType("nonchrome")) {
463 			const char *const argv[] = {
464 				TPM_CLEAR_REQUEST_EXEC_NAME,
465 				NULL,
466 			};
467 			value = subprocess_run(argv, &subprocess_null, &subprocess_null,
468 					       &subprocess_null);
469 		} else {
470 			value = vb2_get_nv_storage(VB2_NV_CLEAR_TPM_OWNER_REQUEST);
471 		}
472 	} else if (!strcasecmp(name,"clear_tpm_owner_done")) {
473 		value = vb2_get_nv_storage(VB2_NV_CLEAR_TPM_OWNER_DONE);
474 	} else if (!strcasecmp(name,"tpm_rebooted")) {
475 		value = vb2_get_nv_storage(VB2_NV_TPM_REQUESTED_REBOOT);
476 	} else if (!strcasecmp(name,"fw_try_count")) {
477 		value = vb2_get_nv_storage(VB2_NV_TRY_COUNT);
478 	} else if (!strcasecmp(name,"fw_vboot2")) {
479 		value = GetVdatInt(VDAT_INT_FW_BOOT2);
480 	} else if (!strcasecmp(name,"fwupdate_tries")) {
481 		value = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD);
482 		if (value != -1)
483 			value &= KERN_NV_FWUPDATE_TRIES_MASK;
484 	} else if (!strcasecmp(name,"block_devmode")) {
485 		value = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD);
486 		if (value != -1) {
487 			value &= KERN_NV_BLOCK_DEVMODE_FLAG;
488 			value = !!value;
489 		}
490 	} else if (!strcasecmp(name,"tpm_attack")) {
491 		value = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD);
492 		if (value != -1) {
493 			value &= KERN_NV_TPM_ATTACK_FLAG;
494 			value = !!value;
495 		}
496 	} else if (!strcasecmp(name,"loc_idx")) {
497 		value = vb2_get_nv_storage(VB2_NV_LOCALIZATION_INDEX);
498 	} else if (!strcasecmp(name,"backup_nvram_request")) {
499 		value = vb2_get_nv_storage(VB2_NV_BACKUP_NVRAM_REQUEST);
500 	} else if (!strcasecmp(name,"dev_boot_usb")) {
501 		value = vb2_get_nv_storage(VB2_NV_DEV_BOOT_EXTERNAL);
502 	} else if (!strcasecmp(name,"dev_boot_altfw") ||
503 		   !strcasecmp(name,"dev_boot_legacy")) {
504 		value = vb2_get_nv_storage(VB2_NV_DEV_BOOT_ALTFW);
505 	} else if (!strcasecmp(name,"dev_boot_signed_only")) {
506 		value = vb2_get_nv_storage(VB2_NV_DEV_BOOT_SIGNED_ONLY);
507 	} else if (!strcasecmp(name,"dev_enable_udc")) {
508 		value = vb2_get_nv_storage(VB2_NV_DEV_ENABLE_UDC);
509 	} else if (!strcasecmp(name,"display_request")) {
510 		value = vb2_get_nv_storage(VB2_NV_DISPLAY_REQUEST);
511 	} else if (!strcasecmp(name,"recovery_subcode")) {
512 		value = vb2_get_nv_storage(VB2_NV_RECOVERY_SUBCODE);
513 	} else if (!strcasecmp(name,"wipeout_request")) {
514 		value = vb2_get_nv_storage(VB2_NV_REQ_WIPEOUT);
515 	} else if (!strcasecmp(name,"kernel_max_rollforward")) {
516 		value = vb2_get_nv_storage(VB2_NV_KERNEL_MAX_ROLLFORWARD);
517 	}
518 	/* Other parameters */
519 	else if (!strcasecmp(name,"cros_debug")) {
520 		value = VbGetCrosDebug();
521 	} else if (!strcasecmp(name,"debug_build")) {
522 		value = VbGetDebugBuild();
523 	} else if (!strcasecmp(name,"devsw_boot")) {
524 		value = GetVdatInt(VDAT_INT_DEVSW_BOOT);
525 	} else if (!strcasecmp(name, "recoverysw_boot")) {
526 		value = GetVdatInt(VDAT_INT_RECSW_BOOT);
527 	} else if (!strcasecmp(name, "wpsw_cur")) {
528 		/* Use "write-protect at boot" as a fallback value. */
529 		value = GetVdatInt(VDAT_INT_HW_WPSW_BOOT);
530 		fprintf(stderr,
531 			"Fallback to WPSW_BOOT (%d), which may be invalid\n",
532 			value);
533 	} else if (!strcasecmp(name,"vdat_flags")) {
534 		value = GetVdatInt(VDAT_INT_FLAGS);
535 	} else if (!strcasecmp(name,"tpm_fwver")) {
536 		value = GetVdatInt(VDAT_INT_FW_VERSION_TPM);
537 	} else if (!strcasecmp(name,"tpm_kernver")) {
538 		value = GetVdatInt(VDAT_INT_KERNEL_VERSION_TPM);
539 	} else if (!strcasecmp(name,"act_fwver")) {
540 		value = GetVdatInt(VDAT_INT_FW_VERSION_ACT);
541 	} else if (!strcasecmp(name,"act_kernver")) {
542 		value = GetVdatInt(VDAT_INT_KERNEL_VERSION_ACT);
543 	} else if (!strcasecmp(name,"recovery_reason")) {
544 		value = GetVdatInt(VDAT_INT_RECOVERY_REASON);
545 	} else if (!strcasecmp(name, "boot_on_ac_detect")) {
546 		value = vb2_get_nv_storage(VB2_NV_BOOT_ON_AC_DETECT);
547 	} else if (!strcasecmp(name, "try_ro_sync")) {
548 		value = vb2_get_nv_storage(VB2_NV_TRY_RO_SYNC);
549 	} else if (!strcasecmp(name, "battery_cutoff_request")) {
550 		value = vb2_get_nv_storage(VB2_NV_BATTERY_CUTOFF_REQUEST);
551 	} else if (!strcasecmp(name, "inside_vm")) {
552 		/* Detect if the host is a VM. If there is no HWID and the
553 		 * firmware type is "nonchrome", then assume it is a VM. If
554 		 * HWID is present, it is a baremetal Chrome OS machine. Other
555 		 * cases are errors. */
556 		char hwid[VB_MAX_STRING_PROPERTY];
557 		if (VbGetSystemPropertyString("hwid", hwid,
558 					      sizeof(hwid)) != 0) {
559 			char fwtype_buf[VB_MAX_STRING_PROPERTY];
560 			int fwtype_ret = VbGetSystemPropertyString(
561 				"mainfw_type", fwtype_buf, sizeof(fwtype_buf));
562 			if (fwtype_ret == 0 &&
563 			    !strcasecmp(fwtype_buf, "nonchrome")) {
564 				value = 1;
565 			}
566 		} else {
567 			value = 0;
568 		}
569 	} else if (!strcasecmp(name, "post_ec_sync_delay")) {
570 		value = vb2_get_nv_storage(VB2_NV_POST_EC_SYNC_DELAY);
571 	}
572 
573 	return value;
574 }
575 
VbGetSystemPropertyString(const char * name,char * dest,size_t size)576 int VbGetSystemPropertyString(const char *name, char *dest, size_t size)
577 {
578 	if (dest == NULL || size == 0)
579 	{
580 		fprintf(stderr, "invalid dest buffer\n");
581 		return -1;
582 	}
583 	/* Check for HWID override via cros_config */
584 	if (!strcasecmp(name, "hwid")) {
585 		char *hwid_override;
586 
587 		if (chromeos_config_get_string("/", "hwid-override",
588 					       &hwid_override) == VB2_SUCCESS) {
589 			StrCopy(dest, hwid_override, size);
590 			free(hwid_override);
591 			return 0;
592 		}
593 	}
594 
595 	/* Check architecture-dependent properties */
596 	if (VbGetArchPropertyString(name, dest, size))
597 		return 0;
598 
599 	if (!strcasecmp(name,"kernkey_vfy")) {
600 		switch(GetVdatInt(VDAT_INT_KERNEL_KEY_VERIFIED)) {
601 			case 0:
602 				StrCopy(dest, "hash", size);
603 				return 0;
604 			case 1:
605 				StrCopy(dest, "sig", size);
606 				return 0;
607 			default:
608 				return -1;
609 		}
610 	} else if (!strcasecmp(name, "mainfw_act")) {
611 		return GetVdatString(dest, size, VDAT_STRING_MAINFW_ACT);
612 	} else if (!strcasecmp(name, "vdat_lfdebug")) {
613 		return GetVdatString(dest, size,
614 				VDAT_STRING_LOAD_FIRMWARE_DEBUG);
615 	} else if (!strcasecmp(name, "fw_try_next")) {
616 		StrCopy(dest,
617 			vb2_get_nv_storage(VB2_NV_TRY_NEXT) ? "B" : "A",
618 			size);
619 		return 0;
620 	} else if (!strcasecmp(name, "fw_tried")) {
621 		StrCopy(dest,
622 			vb2_get_nv_storage(VB2_NV_FW_TRIED) ? "B" : "A",
623 			size);
624 		return 0;
625 	} else if (!strcasecmp(name, "fw_result")) {
626 		int v = vb2_get_nv_storage(VB2_NV_FW_RESULT);
627 		if (v < ARRAY_SIZE(fw_results))
628 			StrCopy(dest, fw_results[v], size);
629 		else
630 			StrCopy(dest, "unknown", size);
631 		return 0;
632 	} else if (!strcasecmp(name, "fw_prev_tried")) {
633 		StrCopy(dest,
634 			vb2_get_nv_storage(VB2_NV_FW_PREV_TRIED) ? "B" : "A",
635 			size);
636 		return 0;
637 	} else if (!strcasecmp(name, "fw_prev_result")) {
638 		int v = vb2_get_nv_storage(VB2_NV_FW_PREV_RESULT);
639 		if (v < ARRAY_SIZE(fw_results))
640 			StrCopy(dest, fw_results[v], size);
641 		else
642 			StrCopy(dest, "unknown", size);
643 		return 0;
644 	} else if (!strcasecmp(name,"dev_default_boot")) {
645 		int v = vb2_get_nv_storage(VB2_NV_DEV_DEFAULT_BOOT);
646 		if (v < ARRAY_SIZE(default_boot))
647 			StrCopy(dest, default_boot[v], size);
648 		else
649 			StrCopy(dest, "unknown", size);
650 		return 0;
651 	} else if (!strcasecmp(name, "minios_priority")) {
652 		StrCopy(dest,
653 			vb2_get_nv_storage(VB2_NV_MINIOS_PRIORITY) ?"B" : "A",
654 			size);
655 		return 0;
656 	}
657 
658 	return -1;
659 }
660 
VbSetSystemPropertyIntInternal(const char * name,int value)661 static int VbSetSystemPropertyIntInternal(const char *name, int value)
662 {
663 	/* Check architecture-dependent properties first */
664 
665 	if (0 == VbSetArchPropertyInt(name, value))
666 		return 0;
667 
668 	/* NV storage values */
669 	if (!strcasecmp(name,"nvram_cleared")) {
670 		/* Can only clear this flag; it's set inside the NV storage
671 		 * library. */
672 		return vb2_set_nv_storage(VB2_NV_KERNEL_SETTINGS_RESET, 0);
673 	} else if (!strcasecmp(name,"recovery_request")) {
674 		return vb2_set_nv_storage(VB2_NV_RECOVERY_REQUEST, value);
675 	} else if (!strcasecmp(name,"diagnostic_request")) {
676 		return vb2_set_nv_storage(VB2_NV_DIAG_REQUEST, value);
677 	} else if (!strcasecmp(name,"recovery_subcode")) {
678 		return vb2_set_nv_storage(VB2_NV_RECOVERY_SUBCODE, value);
679 	} else if (!strcasecmp(name,"dbg_reset")) {
680 		return vb2_set_nv_storage(VB2_NV_DEBUG_RESET_MODE, value);
681 	} else if (!strcasecmp(name,"disable_dev_request")) {
682 		return vb2_set_nv_storage(VB2_NV_DISABLE_DEV_REQUEST, value);
683 	} else if (!strcasecmp(name,"clear_tpm_owner_request")) {
684 		if (EXTERNAL_TPM_CLEAR_REQUEST && CheckFwType("nonchrome")) {
685 			const char *const argv[] = {
686 				TPM_CLEAR_REQUEST_EXEC_NAME,
687 				value ? "1" : "0",
688 				NULL,
689 			};
690 			return subprocess_run(argv, &subprocess_null, &subprocess_null,
691 					      &subprocess_null);
692 		} else {
693 			return vb2_set_nv_storage(
694 				VB2_NV_CLEAR_TPM_OWNER_REQUEST, value);
695 		}
696 	} else if (!strcasecmp(name,"clear_tpm_owner_done")) {
697 		/* Can only clear this flag; it's set by firmware. */
698 		return vb2_set_nv_storage(VB2_NV_CLEAR_TPM_OWNER_DONE, 0);
699 	} else if (!strcasecmp(name,"fw_try_count")) {
700 		return vb2_set_nv_storage(VB2_NV_TRY_COUNT, value);
701 	} else if (!strcasecmp(name,"display_request")) {
702 		return vb2_set_nv_storage(VB2_NV_DISPLAY_REQUEST, value);
703 	} else if (!strcasecmp(name,"wipeout_request")) {
704 		/* Can only clear this flag, set only by firmware. */
705 		return vb2_set_nv_storage(VB2_NV_REQ_WIPEOUT, 0);
706 	} else if (!strcasecmp(name,"backup_nvram_request")) {
707 		/* Best-effort only, since it requires firmware and TPM
708 		 * support. */
709 		return vb2_set_nv_storage(VB2_NV_BACKUP_NVRAM_REQUEST, value);
710 	} else if (!strcasecmp(name,"fwupdate_tries")) {
711 		int kern_nv = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD);
712 		if (kern_nv == -1)
713 			return -1;
714 		kern_nv &= ~KERN_NV_FWUPDATE_TRIES_MASK;
715 		kern_nv |= (value & KERN_NV_FWUPDATE_TRIES_MASK);
716 		return vb2_set_nv_storage_with_backup(
717 			VB2_NV_KERNEL_FIELD, kern_nv);
718 	} else if (!strcasecmp(name,"block_devmode")) {
719 		int kern_nv = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD);
720 		if (kern_nv == -1)
721 			return -1;
722 		kern_nv &= ~KERN_NV_BLOCK_DEVMODE_FLAG;
723 		if (value)
724 			kern_nv |= KERN_NV_BLOCK_DEVMODE_FLAG;
725 		return vb2_set_nv_storage_with_backup(
726 			VB2_NV_KERNEL_FIELD, kern_nv);
727 	} else if (!strcasecmp(name,"tpm_attack")) {
728 		/* This value should only be read and cleared, but we allow
729 		 * setting it to 1 for testing. */
730 		int kern_nv = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD);
731 		if (kern_nv == -1)
732 			return -1;
733 		kern_nv &= ~KERN_NV_TPM_ATTACK_FLAG;
734 		if (value)
735 			kern_nv |= KERN_NV_TPM_ATTACK_FLAG;
736 		return vb2_set_nv_storage_with_backup(
737 			VB2_NV_KERNEL_FIELD, kern_nv);
738 	} else if (!strcasecmp(name,"loc_idx")) {
739 		return vb2_set_nv_storage_with_backup(
740 			VB2_NV_LOCALIZATION_INDEX, value);
741 	} else if (!strcasecmp(name,"dev_boot_usb")) {
742 		return vb2_set_nv_storage_with_backup(
743 			VB2_NV_DEV_BOOT_EXTERNAL, value);
744 	} else if (!strcasecmp(name,"dev_boot_altfw") ||
745 		   !strcasecmp(name,"dev_boot_legacy")) {
746 		return vb2_set_nv_storage_with_backup(
747 			VB2_NV_DEV_BOOT_ALTFW, value);
748 	} else if (!strcasecmp(name,"dev_boot_signed_only")) {
749 		return vb2_set_nv_storage_with_backup(
750 			VB2_NV_DEV_BOOT_SIGNED_ONLY, value);
751 	} else if (!strcasecmp(name, "dev_enable_udc")) {
752 		return vb2_set_nv_storage_with_backup(
753 			VB2_NV_DEV_ENABLE_UDC, value);
754 	} else if (!strcasecmp(name, "boot_on_ac_detect")) {
755 		return vb2_set_nv_storage_with_backup(
756 			VB2_NV_BOOT_ON_AC_DETECT, value);
757 	} else if (!strcasecmp(name, "try_ro_sync")) {
758 		return vb2_set_nv_storage_with_backup(
759 			VB2_NV_TRY_RO_SYNC, value);
760 	} else if (!strcasecmp(name, "battery_cutoff_request")) {
761 		return vb2_set_nv_storage(VB2_NV_BATTERY_CUTOFF_REQUEST, value);
762 	} else if (!strcasecmp(name,"kernel_max_rollforward")) {
763 		return vb2_set_nv_storage(VB2_NV_KERNEL_MAX_ROLLFORWARD, value);
764 	} else if (!strcasecmp(name, "post_ec_sync_delay")) {
765 		return vb2_set_nv_storage(VB2_NV_POST_EC_SYNC_DELAY, value);
766 	}
767 
768 	return -1;
769 }
770 
VbSetSystemPropertyInt(const char * name,int value)771 int VbSetSystemPropertyInt(const char *name, int value)
772 {
773 	int result = -1;
774 	int lock_fd;
775 
776 	lock_fd = AcquireCrossystemLock();
777 	if (lock_fd < 0)
778 		return -1;
779 
780 	result = VbSetSystemPropertyIntInternal(name, value);
781 
782 	if (ReleaseCrossystemLock(lock_fd) < 0)
783 		return -1;
784 
785 	return result;
786 }
787 
VbSetSystemPropertyStringInternal(const char * name,const char * value)788 static int VbSetSystemPropertyStringInternal(const char *name,
789 					     const char *value)
790 {
791 	/* Chain to architecture-dependent properties */
792 	if (0 == VbSetArchPropertyString(name, value))
793 		return 0;
794 
795 	if (!strcasecmp(name, "fw_try_next")) {
796 		if (!strcasecmp(value, "A"))
797 			return vb2_set_nv_storage(VB2_NV_TRY_NEXT, 0);
798 		else if (!strcasecmp(value, "B"))
799 			return vb2_set_nv_storage(VB2_NV_TRY_NEXT, 1);
800 		else
801 			return -1;
802 	} else if (!strcasecmp(name, "minios_priority")) {
803 		if (!strcasecmp(value, "A"))
804 			return vb2_set_nv_storage(VB2_NV_MINIOS_PRIORITY, 0);
805 		else if (!strcasecmp(value, "B"))
806 			return vb2_set_nv_storage(VB2_NV_MINIOS_PRIORITY, 1);
807 		else
808 			return -1;
809 	} else if (!strcasecmp(name, "fw_result")) {
810 		int i;
811 
812 		for (i = 0; i < ARRAY_SIZE(fw_results); i++) {
813 			if (!strcasecmp(value, fw_results[i]))
814 				return vb2_set_nv_storage(VB2_NV_FW_RESULT, i);
815 		}
816 		return -1;
817 	} else if (!strcasecmp(name, "dev_default_boot")) {
818 		int i;
819 
820 		/* "legacy" term deprecated in favour of "altfw"
821 		   (see: b/179458327) */
822 		if (!strcasecmp(value, "legacy")) {
823 			fprintf(stderr,
824 				"!!!\n"
825 				"!!! PLEASE USE 'altfw' INSTEAD OF 'legacy'\n"
826 				"!!!\n");
827 			value = "altfw";
828 		}
829 
830 		for (i = 0; i < ARRAY_SIZE(default_boot); i++) {
831 			if (!strcasecmp(value, default_boot[i]))
832 				return vb2_set_nv_storage(
833 					VB2_NV_DEV_DEFAULT_BOOT, i);
834 		}
835 		return -1;
836 	}
837 
838 	return -1;
839 }
840 
VbSetSystemPropertyString(const char * name,const char * value)841 int VbSetSystemPropertyString(const char *name, const char *value)
842 {
843 	int result = -1;
844 	int lock_fd;
845 
846 	lock_fd = AcquireCrossystemLock();
847 	if (lock_fd < 0)
848 		return -1;
849 
850 	result = VbSetSystemPropertyStringInternal(name, value);
851 
852 	if (ReleaseCrossystemLock(lock_fd) < 0)
853 		return -1;
854 
855 	return result;
856 }
857 
858 /**
859  * Get index of the last valid VBNV entry.
860  *
861  * @param buf		Pointer to the buffer containing VBNV entries.
862  * @param buf_sz	Size of the buffer.
863  * @param vbnv_size	The size of a single VBNV entry for this device.
864  *
865  * @return The index of the last valid VBNV entry found by binary search,
866  * or -1 if not found. When the FMAP region is corrupted (used entries occurring
867  * after blank ones), the returned index may not point to the last VBNV
868  * entry.
869  */
vb2_nv_index(const uint8_t * buf,uint32_t buf_sz,int vbnv_size)870 static int vb2_nv_index(const uint8_t *buf, uint32_t buf_sz, int vbnv_size)
871 {
872 	int used_below, blank_above;
873 	uint8_t blank[VB2_NVDATA_SIZE_V2];
874 
875 	/* The size of the buffer should be an even multiple of the
876 	   VBNV size. */
877 	if (buf_sz % vbnv_size != 0) {
878 		VB2_DIE("The VBNV in flash (%u bytes) is not an even multiple "
879 			"of the VBNV size (%u bytes).  This is likely a "
880 			"firmware bug.\n", buf_sz, vbnv_size);
881 	}
882 
883 	memset(blank, 0xff, sizeof(blank));
884 
885 	/* To match the searching algorithm in firmware, perform binary search
886 	   instead of linear search to find the last used index. */
887 	used_below = 0;
888 	blank_above = buf_sz / vbnv_size;
889 	while (used_below + 1 < blank_above) {
890 		int mid = (used_below + blank_above) / 2;
891 		if (!memcmp(blank, &buf[mid * vbnv_size], vbnv_size))
892 			blank_above = mid;
893 		else
894 			used_below = mid;
895 	}
896 
897 	/* Check the all blank case. */
898 	if (used_below == 0 &&
899 	    !memcmp(blank, &buf[used_below * vbnv_size], vbnv_size)) {
900 		fprintf(stderr, "VBNV is uninitialized.\n");
901 		return -1;
902 	}
903 
904 	return used_below;
905 }
906 
907 /**
908  * Check whether the VBNV entries are corrupted.
909  *
910  * @param buf		Pointer to the buffer containing VBNV entries.
911  * @param buf_sz	Size of the buffer.
912  * @param vbnv_size	The size of a single VBNV entry for this device.
913  *
914  * @return True if there are used entries occurring after blank ones, or false
915  * otherwise.
916  */
is_corrupted(const uint8_t * buf,uint32_t buf_sz,int vbnv_size)917 static bool is_corrupted(const uint8_t *buf, uint32_t buf_sz, int vbnv_size)
918 {
919 	uint8_t blank[VB2_NVDATA_SIZE_V2];
920 	bool found_blank = false;
921 
922 	memset(blank, 0xff, sizeof(blank));
923 
924 	for (int i = 0; i < buf_sz / vbnv_size; i++) {
925 		if (!memcmp(blank, &buf[i * vbnv_size], vbnv_size))
926 			found_blank = true;
927 		else if (found_blank)
928 			return true;
929 	}
930 
931 	return false;
932 }
933 
934 #define VBNV_FMAP_REGION "RW_NVRAM"
935 
vb2_read_nv_storage_flashrom(struct vb2_context * ctx)936 int vb2_read_nv_storage_flashrom(struct vb2_context *ctx)
937 {
938 	int index;
939 	int vbnv_size = vb2_nv_get_size(ctx);
940 
941 	struct firmware_image image = {
942 		.programmer = FLASHROM_PROGRAMMER_INTERNAL_AP,
943 	};
944 	if (flashrom_read(&image, VBNV_FMAP_REGION))
945 		return -1;
946 
947 	index = vb2_nv_index(image.data, image.size, vbnv_size);
948 	if (index < 0) {
949 		free(image.data);
950 		return -1;
951 	}
952 
953 	memcpy(ctx->nvdata, &image.data[index * vbnv_size], vbnv_size);
954 	free(image.data);
955 	return 0;
956 }
957 
vb2_write_nv_storage_flashrom(struct vb2_context * ctx)958 int vb2_write_nv_storage_flashrom(struct vb2_context *ctx)
959 {
960 	int rv = 0;
961 	int index;
962 	bool corrupted;
963 	int vbnv_size = vb2_nv_get_size(ctx);
964 
965 	struct firmware_image image = {
966 		.programmer = FLASHROM_PROGRAMMER_INTERNAL_AP,
967 	};
968 	if (flashrom_read(&image, VBNV_FMAP_REGION))
969 		return -1;
970 
971 	index = vb2_nv_index(image.data, image.size, vbnv_size) + 1;
972 	corrupted = is_corrupted(image.data, image.size, vbnv_size);
973 
974 	if (corrupted || index * vbnv_size == image.size) {
975 		/* VBNV is corrupted or full.  Erase and write at beginning. */
976 		if (corrupted)
977 			fprintf(stderr, "VBNV is corrupted; erasing %s\n",
978 				VBNV_FMAP_REGION);
979 		memset(image.data, 0xff, image.size);
980 		index = 0;
981 	}
982 
983 	memcpy(&image.data[index * vbnv_size], ctx->nvdata, vbnv_size);
984 	if (flashrom_write(&image, VBNV_FMAP_REGION)) {
985 		rv = -1;
986 		goto exit;
987 	}
988 
989  exit:
990 	free(image.data);
991 	return rv;
992 }
993