/* Copyright (c) 2012 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. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "2api.h" #include "2common.h" #include "2nvstorage.h" #include "2sysincludes.h" #include "crossystem_arch.h" #include "crossystem.h" #include "crossystem_vbnv.h" #include "host_common.h" #include "utility.h" #include "vboot_struct.h" /* Filename for kernel command line */ #define KERNEL_CMDLINE_PATH "/proc/cmdline" #define MOSYS_PATH "/usr/sbin/mosys" /* Fields that GetVdatString() can get */ typedef enum VdatStringField { VDAT_STRING_DEPRECATED_TIMERS = 0, /* Timer values */ VDAT_STRING_LOAD_FIRMWARE_DEBUG, /* LoadFirmware() debug information */ VDAT_STRING_LOAD_KERNEL_DEBUG, /* LoadKernel() debug information */ 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_DEPRECATED_DEVSW_VIRTUAL, /* Dev switch is virtual; * deprecated: chromium:942901 */ VDAT_INT_RECSW_BOOT, /* Recovery switch position at boot */ VDAT_INT_RECSW_VIRTUAL, /* Recovery switch is virtual */ 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", "legacy"}; /* 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)); } 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; } #define TRUNCATED "\n(truncated)\n" static char *GetVdatLoadKernelDebug(char *dest, int size, const VbSharedDataHeader *sh) { int used = 0; int first_call_tracked = 0; int call; /* Make sure we have space for truncation warning */ if (size < strlen(TRUNCATED) + 1) return NULL; size -= strlen(TRUNCATED) + 1; used += snprintf( dest + used, size - used, "Calls to LoadKernel()=%d\n", sh->lk_call_count); if (used > size) goto LoadKernelDebugExit; /* Report on the last calls */ if (sh->lk_call_count > VBSD_MAX_KERNEL_CALLS) first_call_tracked = sh->lk_call_count - VBSD_MAX_KERNEL_CALLS; for (call = first_call_tracked; call < sh->lk_call_count; call++) { const VbSharedDataKernelCall* shc = sh->lk_calls + (call & (VBSD_MAX_KERNEL_CALLS - 1)); int first_part_tracked = 0; int part; used += snprintf(dest + used, size - used, "Call %d:\n" " Boot flags=0x%02x\n" " Boot mode=%d\n" " Test error=%d\n" " Return code=%d\n" " Debug flags=0x%02x\n" " Drive sectors=%" PRIu64 "\n" " Sector size=%d\n" " Check result=%d\n" " Kernel partitions found=%d\n", call + 1, shc->boot_flags, shc->boot_mode, shc->test_error_num, shc->return_code, shc->flags, shc->sector_count, shc->sector_size, shc->check_result, shc->kernel_parts_found); if (used > size) goto LoadKernelDebugExit; /* If we found too many partitions, only prints ones where the * structure has info. */ if (shc->kernel_parts_found > VBSD_MAX_KERNEL_PARTS) first_part_tracked = shc->kernel_parts_found - VBSD_MAX_KERNEL_PARTS; /* Report on the partitions checked */ for (part = first_part_tracked; part < shc->kernel_parts_found; part++) { const VbSharedDataKernelPart* shp = shc->parts + (part & (VBSD_MAX_KERNEL_PARTS - 1)); used += snprintf(dest + used, size - used, " Kernel %d:\n" " GPT index=%d\n" " Start sector=%" PRIu64 "\n" " Sector count=%" PRIu64 "\n" " Combined version=0x%08x\n" " Check result=%d\n" " Debug flags=0x%02x\n", part + 1, shp->gpt_index, shp->sector_start, shp->sector_count, shp->combined_version, shp->check_result, shp->flags); if (used > size) goto LoadKernelDebugExit; } } LoadKernelDebugExit: /* Warn if data was truncated; we left space for this above. */ if (used > size) strcat(dest, TRUNCATED); 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_LOAD_KERNEL_DEBUG: value = GetVdatLoadKernelDebug(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_DEPRECATED_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_RECSW_VIRTUAL: value = (sh->flags & VBSD_BOOT_REC_SWITCH_VIRTUAL ? 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")) { 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_USB); } else if (!strcasecmp(name,"dev_boot_legacy")) { value = vb2_get_nv_storage(VB2_NV_DEV_BOOT_LEGACY); } 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, "recoverysw_is_virtual")) { value = GetVdatInt(VDAT_INT_RECSW_VIRTUAL); } else if (!strcasecmp(name, "wpsw_boot")) { value = GetVdatInt(VDAT_INT_HW_WPSW_BOOT); } 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 architecture-dependent properties first */ 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, "vdat_lkdebug")) { return GetVdatString(dest, size, VDAT_STRING_LOAD_KERNEL_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"; } return NULL; } int VbSetSystemPropertyInt(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")) { 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_USB, value); } else if (!strcasecmp(name,"dev_boot_legacy")) { return vb2_set_nv_storage_with_backup( VB2_NV_DEV_BOOT_LEGACY, 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 VbSetSystemPropertyString(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, "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; 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; } static int ExecuteMosys(const char * const argv[], char *buf, size_t bufsize) { int status, mosys_to_crossystem[2]; pid_t pid; ssize_t n; if (pipe(mosys_to_crossystem) < 0) { fprintf(stderr, "pipe() error\n"); return -1; } if ((pid = fork()) < 0) { fprintf(stderr, "fork() error\n"); close(mosys_to_crossystem[0]); close(mosys_to_crossystem[1]); return -1; } else if (!pid) { /* Child */ close(mosys_to_crossystem[0]); /* Redirect pipe's write-end to mosys' stdout */ if (STDOUT_FILENO != mosys_to_crossystem[1]) { if (dup2(mosys_to_crossystem[1], STDOUT_FILENO) != STDOUT_FILENO) { fprintf(stderr, "stdout dup2() failed (mosys)\n"); close(mosys_to_crossystem[1]); exit(1); } } /* Execute mosys (needs cast because POSIX is stupid) */ execv(MOSYS_PATH, (char * const *)argv); /* We shouldn't be here; exit now! */ fprintf(stderr, "execv() of mosys failed\n"); close(mosys_to_crossystem[1]); exit(1); } else { /* Parent */ close(mosys_to_crossystem[1]); if (bufsize) { bufsize--; /* Reserve 1 byte for '\0' */ while ((n = read(mosys_to_crossystem[0], buf, bufsize)) > 0) { buf += n; bufsize -= n; } *buf = '\0'; } else { n = 0; } close(mosys_to_crossystem[0]); if (n < 0) fprintf(stderr, "read() error on output from mosys\n"); if (waitpid(pid, &status, 0) < 0 || status) { fprintf(stderr, "waitpid() or mosys error\n"); return -1; } if (n < 0) return -1; } return 0; } int vb2_read_nv_storage_mosys(struct vb2_context *ctx) { /* Reserve extra 32 bytes */ char hexstring[VB2_NVDATA_SIZE_V2 * 2 + 32]; /* * TODO(rspangler): mosys doesn't know how to read anything but 16-byte * records yet. When it grows a command line option to do that, call * it here when needed. * * It's possible mosys won't need that. For example, if if examines * the header byte to determine the records size, or if it calls back * to crossystem to read the VBSD flag. */ const char * const argv[] = { MOSYS_PATH, "nvram", "vboot", "read", NULL }; char hexdigit[3]; const int nvsize = vb2_nv_get_size(ctx); int i; if (ExecuteMosys(argv, hexstring, sizeof(hexstring))) return -1; if (strlen(hexstring) < 2 * nvsize) { fprintf(stderr, "mosys returned hex nvdata size %d" " (need %d)\n", (int)strlen(hexstring), 2 * nvsize); return -1; } hexdigit[2] = '\0'; for (i = 0; i < nvsize; i++) { hexdigit[0] = hexstring[i * 2]; hexdigit[1] = hexstring[i * 2 + 1]; ctx->nvdata[i] = strtol(hexdigit, NULL, 16); } return 0; } int vb2_write_nv_storage_mosys(struct vb2_context *ctx) { char hexstring[VB2_NVDATA_SIZE_V2 * 2 + 1]; const char * const argv[] = { MOSYS_PATH, "nvram", "vboot", "write", hexstring, NULL }; const int nvsize = vb2_nv_get_size(ctx); int i; for (i = 0; i < nvsize; i++) snprintf(hexstring + i * 2, 3, "%02x", ctx->nvdata[i]); hexstring[sizeof(hexstring) - 1] = '\0'; if (ExecuteMosys(argv, NULL, 0)) return -1; return 0; }