/* Copyright 2012 The ChromiumOS Authors * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include #include #include #include #include #include #include #include "2api.h" #include "2common.h" #include "2nvstorage.h" #include "2sysincludes.h" #include "chromeos_config.h" #include "crossystem_arch.h" #include "crossystem.h" #include "crossystem_vbnv.h" #include "host_common.h" #include "flashrom.h" #include "subprocess.h" #include "vboot_struct.h" /* Filename for crossystem lock */ #define CROSSYSTEM_LOCK_PATH "/run/lock/crossystem.lock" /* Filename for kernel command line */ #define KERNEL_CMDLINE_PATH "/proc/cmdline" /* Filename for the TPM simulator NV data */ #define TPM_SIMULATOR_NVCHIP_PATH \ "/mnt/stateful_partition/unencrypted/tpm2-simulator/NVChip" /* Fields that GetVdatString() can get */ typedef enum VdatStringField { VDAT_STRING_DEPRECATED_TIMERS = 0, /* Timer values */ VDAT_STRING_LOAD_FIRMWARE_DEBUG, /* LoadFirmware() debug info */ VDAT_STRING_DEPRECATED_LOAD_KERNEL_DEBUG, /* vb2api_load_kernel() debug info */ VDAT_STRING_MAINFW_ACT /* Active main firmware */ } VdatStringField; /* Fields that GetVdatInt() can get */ typedef enum VdatIntField { VDAT_INT_FLAGS = 0, /* Flags */ VDAT_INT_HEADER_VERSION, /* Header version for VbSharedData */ VDAT_INT_DEVSW_BOOT, /* Dev switch position at boot */ VDAT_INT_RECSW_BOOT, /* Recovery switch position at boot */ VDAT_INT_HW_WPSW_BOOT, /* Hardware WP switch position at boot */ VDAT_INT_FW_VERSION_TPM, /* Current firmware version in TPM */ VDAT_INT_KERNEL_VERSION_TPM, /* Current kernel version in TPM */ VDAT_INT_TRIED_FIRMWARE_B, /* Tried firmware B due to fwb_tries */ VDAT_INT_KERNEL_KEY_VERIFIED, /* Kernel key verified using * signature, not just hash */ VDAT_INT_RECOVERY_REASON, /* Recovery reason for current boot */ VDAT_INT_FW_BOOT2 /* Firmware selection by vboot2 */ } VdatIntField; /* Description of build options that may be specified on the * kernel command line. */ typedef enum VbBuildOption { VB_BUILD_OPTION_UNKNOWN, VB_BUILD_OPTION_DEBUG, VB_BUILD_OPTION_NODEBUG } VbBuildOption; static const char *fw_results[] = {"unknown", "trying", "success", "failure"}; static const char *default_boot[] = {"disk", "usb", "altfw"}; /* Masks for kern_nv usage by kernel. */ #define KERN_NV_FWUPDATE_TRIES_MASK 0x000F #define KERN_NV_BLOCK_DEVMODE_FLAG 0x0010 #define KERN_NV_TPM_ATTACK_FLAG 0x0020 /* If you want to use the remaining currently-unused bits in kern_nv * for something kernel-y, define a new field (the way we did for * fwupdate_tries). Don't just modify kern_nv directly, because that * makes it too easy to accidentally corrupt other sub-fields. */ #define KERN_NV_CURRENTLY_UNUSED 0xFFC0 /* Return true if the FWID starts with the specified string. */ int FwidStartsWith(const char *start) { char fwid[VB_MAX_STRING_PROPERTY]; if (!VbGetSystemPropertyString("fwid", fwid, sizeof(fwid))) return 0; return 0 == strncmp(fwid, start, strlen(start)); } /* Acquire the lock for crossystem SetSystemProperty call. */ static int AcquireCrossystemLock(void) { int lock_fd; lock_fd = open(CROSSYSTEM_LOCK_PATH, O_RDWR | O_CREAT, 0600); if (lock_fd < 0) return -1; if (flock(lock_fd, LOCK_EX) < 0) return -1; return lock_fd; } /* Release the lock for crossystem SetSystemProperty call. */ static int ReleaseCrossystemLock(int lock_fd) { if (flock(lock_fd, F_UNLCK) < 0) return -1; close(lock_fd); return 0; } static struct vb2_context *get_fake_context(void) { static uint8_t fake_workbuf[sizeof(struct vb2_shared_data) + 16] __attribute__((aligned(VB2_WORKBUF_ALIGN))); static struct vb2_context *fake_ctx; if (fake_ctx) return fake_ctx; vb2api_init(fake_workbuf, sizeof(fake_workbuf), &fake_ctx); return fake_ctx; } static int vnc_read; int vb2_get_nv_storage(enum vb2_nv_param param) { VbSharedDataHeader* sh = VbSharedDataRead(); struct vb2_context *ctx = get_fake_context(); if (!sh) return -1; /* TODO: locking around NV access */ if (!vnc_read) { if (sh && sh->flags & VBSD_NVDATA_V2) ctx->flags |= VB2_CONTEXT_NVDATA_V2; if (0 != vb2_read_nv_storage(ctx)) { free(sh); return -1; } vb2_nv_init(ctx); /* TODO: If vnc.raw_changed, attempt to reopen NVRAM for write * and save the new defaults. If we're able to, log. */ vnc_read = 1; } free(sh); return (int)vb2_nv_get(ctx, param); } int vb2_set_nv_storage(enum vb2_nv_param param, int value) { VbSharedDataHeader* sh = VbSharedDataRead(); struct vb2_context *ctx = get_fake_context(); if (!sh) return -1; /* TODO: locking around NV access */ if (sh && sh->flags & VBSD_NVDATA_V2) ctx->flags |= VB2_CONTEXT_NVDATA_V2; if (0 != vb2_read_nv_storage(ctx)) { free(sh); return -1; } vb2_nv_init(ctx); vb2_nv_set(ctx, param, (uint32_t)value); if (ctx->flags & VB2_CONTEXT_NVDATA_CHANGED) { vnc_read = 0; if (0 != vb2_write_nv_storage(ctx)) { free(sh); return -1; } } /* Success */ free(sh); return 0; } /* * Set a param value, and try to flag it for persistent backup. It's okay if * backup isn't supported (which it isn't, in current designs). It's * best-effort only. */ static int vb2_set_nv_storage_with_backup(enum vb2_nv_param param, int value) { int retval; retval = vb2_set_nv_storage(param, value); if (!retval) vb2_set_nv_storage(VB2_NV_BACKUP_NVRAM_REQUEST, 1); return retval; } /* Find what build/debug status is specified on the kernel command * line, if any. */ static VbBuildOption VbScanBuildOption(void) { FILE* f = NULL; char buf[4096] = ""; char *t, *saveptr; const char *delimiters = " \r\n"; f = fopen(KERNEL_CMDLINE_PATH, "r"); if (NULL != f) { if (NULL == fgets(buf, sizeof(buf), f)) buf[0] = 0; fclose(f); } for (t = strtok_r(buf, delimiters, &saveptr); t; t = strtok_r(NULL, delimiters, &saveptr)) { if (0 == strcmp(t, "cros_debug")) return VB_BUILD_OPTION_DEBUG; else if (0 == strcmp(t, "cros_nodebug")) return VB_BUILD_OPTION_NODEBUG; } return VB_BUILD_OPTION_UNKNOWN; } /* Determine whether the running OS image was built for debugging. * Returns 1 if yes, 0 if no or indeterminate. */ static vb2_error_t VbGetDebugBuild(void) { return VB_BUILD_OPTION_DEBUG == VbScanBuildOption(); } /* Determine whether OS-level debugging should be allowed. * Returns 1 if yes, 0 if no or indeterminate. */ static int VbGetCrosDebug(void) { /* If the currently running system specifies its debug status, use * that in preference to other indicators. */ VbBuildOption option = VbScanBuildOption(); if (VB_BUILD_OPTION_DEBUG == option) { return 1; } else if (VB_BUILD_OPTION_NODEBUG == option) { return 0; } /* Command line is silent; allow debug if the dev switch is on. */ if (1 == VbGetSystemPropertyInt("devsw_boot")) return 1; /* All other cases disallow debug. */ return 0; } static char *GetVdatLoadFirmwareDebug(char *dest, int size, const VbSharedDataHeader *sh) { snprintf(dest, size, "Check A result=%d\n" "Check B result=%d\n" "Firmware index booted=0x%02x\n" "TPM combined version at start=0x%08x\n" "Lowest combined version from firmware=0x%08x\n", sh->check_fw_a_result, sh->check_fw_b_result, sh->firmware_index, sh->fw_version_tpm_start, sh->fw_version_lowest); return dest; } static char *GetVdatString(char *dest, int size, VdatStringField field) { VbSharedDataHeader *sh = VbSharedDataRead(); char *value = dest; if (!sh) return NULL; switch (field) { case VDAT_STRING_LOAD_FIRMWARE_DEBUG: value = GetVdatLoadFirmwareDebug(dest, size, sh); break; case VDAT_STRING_MAINFW_ACT: switch(sh->firmware_index) { case 0: StrCopy(dest, "A", size); break; case 1: StrCopy(dest, "B", size); break; case 0xFF: StrCopy(dest, "recovery", size); break; default: value = NULL; } break; default: value = NULL; break; } free(sh); return value; } static int GetVdatInt(VdatIntField field) { VbSharedDataHeader* sh = VbSharedDataRead(); int value = -1; if (!sh) return -1; /* Fields supported in version 1 */ switch (field) { case VDAT_INT_FLAGS: value = (int)sh->flags; break; case VDAT_INT_HEADER_VERSION: value = sh->struct_version; break; case VDAT_INT_TRIED_FIRMWARE_B: value = (sh->flags & VBSD_FWB_TRIED ? 1 : 0); break; case VDAT_INT_KERNEL_KEY_VERIFIED: value = (sh->flags & VBSD_KERNEL_KEY_VERIFIED ? 1 : 0); break; case VDAT_INT_FW_VERSION_TPM: value = (int)sh->fw_version_tpm; break; case VDAT_INT_KERNEL_VERSION_TPM: value = (int)sh->kernel_version_tpm; break; case VDAT_INT_FW_BOOT2: value = (sh->flags & VBSD_BOOT_FIRMWARE_VBOOT2 ? 1 : 0); VBOOT_FALLTHROUGH; default: break; } /* Fields added in struct version 2 */ if (sh->struct_version >= 2) { switch(field) { case VDAT_INT_DEVSW_BOOT: value = (sh->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0); break; case VDAT_INT_RECSW_BOOT: value = (sh->flags & VBSD_BOOT_REC_SWITCH_ON ? 1 : 0); break; case VDAT_INT_HW_WPSW_BOOT: value = (sh->flags & VBSD_BOOT_FIRMWARE_WP_ENABLED ? 1 : 0); break; case VDAT_INT_RECOVERY_REASON: value = sh->recovery_reason; break; default: break; } } free(sh); return value; } /* Return version of VbSharedData struct or -1 if not found. */ int VbSharedDataVersion(void) { return GetVdatInt(VDAT_INT_HEADER_VERSION); } int VbGetSystemPropertyInt(const char *name) { int value = -1; /* Check architecture-dependent properties first */ value = VbGetArchPropertyInt(name); if (-1 != value) return value; /* NV storage values */ else if (!strcasecmp(name,"kern_nv")) { value = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD); } else if (!strcasecmp(name,"nvram_cleared")) { value = vb2_get_nv_storage(VB2_NV_KERNEL_SETTINGS_RESET); } else if (!strcasecmp(name,"recovery_request")) { value = vb2_get_nv_storage(VB2_NV_RECOVERY_REQUEST); } else if (!strcasecmp(name,"diagnostic_request")) { value = vb2_get_nv_storage(VB2_NV_DIAG_REQUEST); } else if (!strcasecmp(name,"dbg_reset")) { value = vb2_get_nv_storage(VB2_NV_DEBUG_RESET_MODE); } else if (!strcasecmp(name,"disable_dev_request")) { value = vb2_get_nv_storage(VB2_NV_DISABLE_DEV_REQUEST); } else if (!strcasecmp(name,"clear_tpm_owner_request")) { if (TPM2_SIMULATOR) /* Check TPM simulator NVChip status */ value = access(TPM_SIMULATOR_NVCHIP_PATH, F_OK) != 0; else value = vb2_get_nv_storage( VB2_NV_CLEAR_TPM_OWNER_REQUEST); } else if (!strcasecmp(name,"clear_tpm_owner_done")) { value = vb2_get_nv_storage(VB2_NV_CLEAR_TPM_OWNER_DONE); } else if (!strcasecmp(name,"tpm_rebooted")) { value = vb2_get_nv_storage(VB2_NV_TPM_REQUESTED_REBOOT); } else if (!strcasecmp(name,"fwb_tries") || !strcasecmp(name,"fw_try_count")) { value = vb2_get_nv_storage(VB2_NV_TRY_COUNT); } else if (!strcasecmp(name,"fw_vboot2")) { value = GetVdatInt(VDAT_INT_FW_BOOT2); } else if (!strcasecmp(name,"fwupdate_tries")) { value = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD); if (value != -1) value &= KERN_NV_FWUPDATE_TRIES_MASK; } else if (!strcasecmp(name,"block_devmode")) { value = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD); if (value != -1) { value &= KERN_NV_BLOCK_DEVMODE_FLAG; value = !!value; } } else if (!strcasecmp(name,"tpm_attack")) { value = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD); if (value != -1) { value &= KERN_NV_TPM_ATTACK_FLAG; value = !!value; } } else if (!strcasecmp(name,"loc_idx")) { value = vb2_get_nv_storage(VB2_NV_LOCALIZATION_INDEX); } else if (!strcasecmp(name,"backup_nvram_request")) { value = vb2_get_nv_storage(VB2_NV_BACKUP_NVRAM_REQUEST); } else if (!strcasecmp(name,"dev_boot_usb")) { value = vb2_get_nv_storage(VB2_NV_DEV_BOOT_EXTERNAL); } else if (!strcasecmp(name,"dev_boot_altfw") || !strcasecmp(name,"dev_boot_legacy")) { value = vb2_get_nv_storage(VB2_NV_DEV_BOOT_ALTFW); } else if (!strcasecmp(name,"dev_boot_signed_only")) { value = vb2_get_nv_storage(VB2_NV_DEV_BOOT_SIGNED_ONLY); } else if (!strcasecmp(name,"dev_enable_udc")) { value = vb2_get_nv_storage(VB2_NV_DEV_ENABLE_UDC); } else if (!strcasecmp(name,"display_request")) { value = vb2_get_nv_storage(VB2_NV_DISPLAY_REQUEST); } else if (!strcasecmp(name,"recovery_subcode")) { value = vb2_get_nv_storage(VB2_NV_RECOVERY_SUBCODE); } else if (!strcasecmp(name,"wipeout_request")) { value = vb2_get_nv_storage(VB2_NV_REQ_WIPEOUT); } else if (!strcasecmp(name,"kernel_max_rollforward")) { value = vb2_get_nv_storage(VB2_NV_KERNEL_MAX_ROLLFORWARD); } /* Other parameters */ else if (!strcasecmp(name,"cros_debug")) { value = VbGetCrosDebug(); } else if (!strcasecmp(name,"debug_build")) { value = VbGetDebugBuild(); } else if (!strcasecmp(name,"devsw_boot")) { value = GetVdatInt(VDAT_INT_DEVSW_BOOT); } else if (!strcasecmp(name, "recoverysw_boot")) { value = GetVdatInt(VDAT_INT_RECSW_BOOT); } else if (!strcasecmp(name, "wpsw_cur")) { /* Use "write-protect at boot" as a fallback value. */ value = GetVdatInt(VDAT_INT_HW_WPSW_BOOT); fprintf(stderr, "Fallback to WPSW_BOOT (%d), which may be invalid\n", value); } else if (!strcasecmp(name,"vdat_flags")) { value = GetVdatInt(VDAT_INT_FLAGS); } else if (!strcasecmp(name,"tpm_fwver")) { value = GetVdatInt(VDAT_INT_FW_VERSION_TPM); } else if (!strcasecmp(name,"tpm_kernver")) { value = GetVdatInt(VDAT_INT_KERNEL_VERSION_TPM); } else if (!strcasecmp(name,"tried_fwb")) { value = GetVdatInt(VDAT_INT_TRIED_FIRMWARE_B); } else if (!strcasecmp(name,"recovery_reason")) { value = GetVdatInt(VDAT_INT_RECOVERY_REASON); } else if (!strcasecmp(name, "boot_on_ac_detect")) { value = vb2_get_nv_storage(VB2_NV_BOOT_ON_AC_DETECT); } else if (!strcasecmp(name, "try_ro_sync")) { value = vb2_get_nv_storage(VB2_NV_TRY_RO_SYNC); } else if (!strcasecmp(name, "battery_cutoff_request")) { value = vb2_get_nv_storage(VB2_NV_BATTERY_CUTOFF_REQUEST); } else if (!strcasecmp(name, "inside_vm")) { /* Detect if the host is a VM. If there is no HWID and the * firmware type is "nonchrome", then assume it is a VM. If * HWID is present, it is a baremetal Chrome OS machine. Other * cases are errors. */ char hwid[VB_MAX_STRING_PROPERTY]; if (!VbGetSystemPropertyString("hwid", hwid, sizeof(hwid))) { char fwtype_buf[VB_MAX_STRING_PROPERTY]; const char *fwtype = VbGetSystemPropertyString( "mainfw_type", fwtype_buf, sizeof(fwtype_buf)); if (fwtype && !strcasecmp(fwtype, "nonchrome")) { value = 1; } } else { value = 0; } } else if (!strcasecmp(name, "post_ec_sync_delay")) { value = vb2_get_nv_storage(VB2_NV_POST_EC_SYNC_DELAY); } return value; } const char *VbGetSystemPropertyString(const char *name, char *dest, size_t size) { /* Check for HWID override via cros_config */ if (!strcasecmp(name, "hwid")) { char *hwid_override; if (chromeos_config_get_string("/", "hwid-override", &hwid_override) == VB2_SUCCESS) { StrCopy(dest, hwid_override, size); free(hwid_override); return dest; } } /* Check architecture-dependent properties */ if (VbGetArchPropertyString(name, dest, size)) return dest; if (!strcasecmp(name,"kernkey_vfy")) { switch(GetVdatInt(VDAT_INT_KERNEL_KEY_VERIFIED)) { case 0: return "hash"; case 1: return "sig"; default: return NULL; } } else if (!strcasecmp(name, "mainfw_act")) { return GetVdatString(dest, size, VDAT_STRING_MAINFW_ACT); } else if (!strcasecmp(name, "vdat_lfdebug")) { return GetVdatString(dest, size, VDAT_STRING_LOAD_FIRMWARE_DEBUG); } else if (!strcasecmp(name, "fw_try_next")) { return vb2_get_nv_storage(VB2_NV_TRY_NEXT) ? "B" : "A"; } else if (!strcasecmp(name, "fw_tried")) { return vb2_get_nv_storage(VB2_NV_FW_TRIED) ? "B" : "A"; } else if (!strcasecmp(name, "fw_result")) { int v = vb2_get_nv_storage(VB2_NV_FW_RESULT); if (v < ARRAY_SIZE(fw_results)) return fw_results[v]; else return "unknown"; } else if (!strcasecmp(name, "fw_prev_tried")) { return vb2_get_nv_storage(VB2_NV_FW_PREV_TRIED) ? "B" : "A"; } else if (!strcasecmp(name, "fw_prev_result")) { int v = vb2_get_nv_storage(VB2_NV_FW_PREV_RESULT); if (v < ARRAY_SIZE(fw_results)) return fw_results[v]; else return "unknown"; } else if (!strcasecmp(name,"dev_default_boot")) { int v = vb2_get_nv_storage(VB2_NV_DEV_DEFAULT_BOOT); if (v < ARRAY_SIZE(default_boot)) return default_boot[v]; else return "unknown"; } else if (!strcasecmp(name, "minios_priority")) { return vb2_get_nv_storage(VB2_NV_MINIOS_PRIORITY) ? "B" : "A"; } return NULL; } static int VbSetSystemPropertyIntInternal(const char *name, int value) { /* Check architecture-dependent properties first */ if (0 == VbSetArchPropertyInt(name, value)) return 0; /* NV storage values */ if (!strcasecmp(name,"nvram_cleared")) { /* Can only clear this flag; it's set inside the NV storage * library. */ return vb2_set_nv_storage(VB2_NV_KERNEL_SETTINGS_RESET, 0); } else if (!strcasecmp(name,"recovery_request")) { return vb2_set_nv_storage(VB2_NV_RECOVERY_REQUEST, value); } else if (!strcasecmp(name,"diagnostic_request")) { return vb2_set_nv_storage(VB2_NV_DIAG_REQUEST, value); } else if (!strcasecmp(name,"recovery_subcode")) { return vb2_set_nv_storage(VB2_NV_RECOVERY_SUBCODE, value); } else if (!strcasecmp(name,"dbg_reset")) { return vb2_set_nv_storage(VB2_NV_DEBUG_RESET_MODE, value); } else if (!strcasecmp(name,"disable_dev_request")) { return vb2_set_nv_storage(VB2_NV_DISABLE_DEV_REQUEST, value); } else if (!strcasecmp(name,"clear_tpm_owner_request")) { if (TPM2_SIMULATOR) { /* We don't support to set clear_tpm_owner_request to 0 * on simulator */ if (value == 0) return -1; /* Check TPM simulator data status */ if (!access(TPM_SIMULATOR_NVCHIP_PATH, F_OK)) { /* Remove the TPM2.0 simulator data */ return remove(TPM_SIMULATOR_NVCHIP_PATH); } else { /* Return success when the file is already * removed */ return 0; } } else { return vb2_set_nv_storage( VB2_NV_CLEAR_TPM_OWNER_REQUEST, value); } } else if (!strcasecmp(name,"clear_tpm_owner_done")) { /* Can only clear this flag; it's set by firmware. */ return vb2_set_nv_storage(VB2_NV_CLEAR_TPM_OWNER_DONE, 0); } else if (!strcasecmp(name,"fwb_tries") || !strcasecmp(name,"fw_try_count")) { return vb2_set_nv_storage(VB2_NV_TRY_COUNT, value); } else if (!strcasecmp(name,"display_request")) { return vb2_set_nv_storage(VB2_NV_DISPLAY_REQUEST, value); } else if (!strcasecmp(name,"wipeout_request")) { /* Can only clear this flag, set only by firmware. */ return vb2_set_nv_storage(VB2_NV_REQ_WIPEOUT, 0); } else if (!strcasecmp(name,"backup_nvram_request")) { /* Best-effort only, since it requires firmware and TPM * support. */ return vb2_set_nv_storage(VB2_NV_BACKUP_NVRAM_REQUEST, value); } else if (!strcasecmp(name,"fwupdate_tries")) { int kern_nv = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD); if (kern_nv == -1) return -1; kern_nv &= ~KERN_NV_FWUPDATE_TRIES_MASK; kern_nv |= (value & KERN_NV_FWUPDATE_TRIES_MASK); return vb2_set_nv_storage_with_backup( VB2_NV_KERNEL_FIELD, kern_nv); } else if (!strcasecmp(name,"block_devmode")) { int kern_nv = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD); if (kern_nv == -1) return -1; kern_nv &= ~KERN_NV_BLOCK_DEVMODE_FLAG; if (value) kern_nv |= KERN_NV_BLOCK_DEVMODE_FLAG; return vb2_set_nv_storage_with_backup( VB2_NV_KERNEL_FIELD, kern_nv); } else if (!strcasecmp(name,"tpm_attack")) { /* This value should only be read and cleared, but we allow * setting it to 1 for testing. */ int kern_nv = vb2_get_nv_storage(VB2_NV_KERNEL_FIELD); if (kern_nv == -1) return -1; kern_nv &= ~KERN_NV_TPM_ATTACK_FLAG; if (value) kern_nv |= KERN_NV_TPM_ATTACK_FLAG; return vb2_set_nv_storage_with_backup( VB2_NV_KERNEL_FIELD, kern_nv); } else if (!strcasecmp(name,"loc_idx")) { return vb2_set_nv_storage_with_backup( VB2_NV_LOCALIZATION_INDEX, value); } else if (!strcasecmp(name,"dev_boot_usb")) { return vb2_set_nv_storage_with_backup( VB2_NV_DEV_BOOT_EXTERNAL, value); } else if (!strcasecmp(name,"dev_boot_altfw") || !strcasecmp(name,"dev_boot_legacy")) { return vb2_set_nv_storage_with_backup( VB2_NV_DEV_BOOT_ALTFW, value); } else if (!strcasecmp(name,"dev_boot_signed_only")) { return vb2_set_nv_storage_with_backup( VB2_NV_DEV_BOOT_SIGNED_ONLY, value); } else if (!strcasecmp(name, "dev_enable_udc")) { return vb2_set_nv_storage_with_backup( VB2_NV_DEV_ENABLE_UDC, value); } else if (!strcasecmp(name, "boot_on_ac_detect")) { return vb2_set_nv_storage_with_backup( VB2_NV_BOOT_ON_AC_DETECT, value); } else if (!strcasecmp(name, "try_ro_sync")) { return vb2_set_nv_storage_with_backup( VB2_NV_TRY_RO_SYNC, value); } else if (!strcasecmp(name, "battery_cutoff_request")) { return vb2_set_nv_storage(VB2_NV_BATTERY_CUTOFF_REQUEST, value); } else if (!strcasecmp(name,"kernel_max_rollforward")) { return vb2_set_nv_storage(VB2_NV_KERNEL_MAX_ROLLFORWARD, value); } else if (!strcasecmp(name, "post_ec_sync_delay")) { return vb2_set_nv_storage(VB2_NV_POST_EC_SYNC_DELAY, value); } return -1; } int VbSetSystemPropertyInt(const char *name, int value) { int result = -1; int lock_fd; lock_fd = AcquireCrossystemLock(); if (lock_fd < 0) return -1; result = VbSetSystemPropertyIntInternal(name, value); if (ReleaseCrossystemLock(lock_fd) < 0) return -1; return result; } static int VbSetSystemPropertyStringInternal(const char *name, const char *value) { /* Chain to architecture-dependent properties */ if (0 == VbSetArchPropertyString(name, value)) return 0; if (!strcasecmp(name, "fw_try_next")) { if (!strcasecmp(value, "A")) return vb2_set_nv_storage(VB2_NV_TRY_NEXT, 0); else if (!strcasecmp(value, "B")) return vb2_set_nv_storage(VB2_NV_TRY_NEXT, 1); else return -1; } else if (!strcasecmp(name, "minios_priority")) { if (!strcasecmp(value, "A")) return vb2_set_nv_storage(VB2_NV_MINIOS_PRIORITY, 0); else if (!strcasecmp(value, "B")) return vb2_set_nv_storage(VB2_NV_MINIOS_PRIORITY, 1); else return -1; } else if (!strcasecmp(name, "fw_result")) { int i; for (i = 0; i < ARRAY_SIZE(fw_results); i++) { if (!strcasecmp(value, fw_results[i])) return vb2_set_nv_storage(VB2_NV_FW_RESULT, i); } return -1; } else if (!strcasecmp(name, "dev_default_boot")) { int i; /* "legacy" term deprecated in favour of "altfw" (see: b/179458327) */ if (!strcasecmp(value, "legacy")) { fprintf(stderr, "!!!\n" "!!! PLEASE USE 'altfw' INSTEAD OF 'legacy'\n" "!!!\n"); value = "altfw"; } for (i = 0; i < ARRAY_SIZE(default_boot); i++) { if (!strcasecmp(value, default_boot[i])) return vb2_set_nv_storage( VB2_NV_DEV_DEFAULT_BOOT, i); } return -1; } return -1; } int VbSetSystemPropertyString(const char *name, const char *value) { int result = -1; int lock_fd; lock_fd = AcquireCrossystemLock(); if (lock_fd < 0) return -1; result = VbSetSystemPropertyStringInternal(name, value); if (ReleaseCrossystemLock(lock_fd) < 0) return -1; return result; } /** * Get index of the last valid VBNV entry in an EEPROM. * * @param buf Pointer to the beginning of the EEPROM. * @param buf_sz Size of the EEPROM. * @param vbnv_size The size of a single VBNV entry for this device. * * @return The index of the last valid VBNV entry on success, or -1 on * failure. */ static int vb2_nv_index(const uint8_t *buf, uint32_t buf_sz, int vbnv_size) { int index; uint8_t blank[VB2_NVDATA_SIZE_V2]; /* The size of the buffer should be an even multiple of the VBNV size. */ if (buf_sz % vbnv_size != 0) { VB2_DIE("The VBNV in flash (%u bytes) is not an even multiple " "of the VBNV size (%u bytes). This is likely a " "firmware bug.\n", buf_sz, vbnv_size); } memset(blank, 0xff, sizeof(blank)); for (index = 0; index < buf_sz / vbnv_size; index++) { if (!memcmp(blank, &buf[index * vbnv_size], vbnv_size)) break; } if (!index) { fprintf(stderr, "VBNV is uninitialized.\n"); return -1; } return index - 1; } #define VBNV_FMAP_REGION "RW_NVRAM" int vb2_read_nv_storage_flashrom(struct vb2_context *ctx) { int index; int vbnv_size = vb2_nv_get_size(ctx); struct firmware_image image = { .programmer = FLASHROM_PROGRAMMER_INTERNAL_AP, }; if (flashrom_read(&image, VBNV_FMAP_REGION)) return -1; index = vb2_nv_index(image.data, image.size, vbnv_size); if (index < 0) { free(image.data); return -1; } memcpy(ctx->nvdata, &image.data[index * vbnv_size], vbnv_size); free(image.data); return 0; } int vb2_write_nv_storage_flashrom(struct vb2_context *ctx) { int rv = 0; int current_index; int next_index; int vbnv_size = vb2_nv_get_size(ctx); struct firmware_image image = { .programmer = FLASHROM_PROGRAMMER_INTERNAL_AP, }; if (flashrom_read(&image, VBNV_FMAP_REGION)) return -1; current_index = vb2_nv_index(image.data, image.size, vbnv_size); if (current_index < 0) { rv = -1; goto exit; } next_index = current_index + 1; if (next_index * vbnv_size == image.size) { /* VBNV is full. Erase and write at beginning. */ memset(image.data, 0xff, image.size); next_index = 0; } memcpy(&image.data[next_index * vbnv_size], ctx->nvdata, vbnv_size); if (flashrom_write(&image, VBNV_FMAP_REGION)) { rv = -1; goto exit; } exit: free(image.data); return rv; }