int RestoreNvFromBackup(VbNvContext *vnc) { VbNvContext bvnc; uint32_t value; int i; VBDEBUG(("TPM: %s()\n", __func__)); if (TPM_SUCCESS != RollbackBackupRead(bvnc.raw)) return 1; VbNvSetup(&bvnc); if (bvnc.regenerate_crc) { VBDEBUG(("TPM: Oops, backup is no good.\n")); return 1; } for (i = 0; i < ARRAY_SIZE(backup_params); i++) { VbNvGet(&bvnc, backup_params[i], &value); VbNvSet(vnc, backup_params[i], value); } /* VbNvTeardown(&bvnc); is not needed. We're done with it. */ return 0; }
int VbGetNvStorage(VbNvParam param) { uint32_t value; int retval; static VbNvContext cached_vnc; /* TODO: locking around NV access */ if (!vnc_read) { if (0 != VbReadNvStorage(&cached_vnc)) return -1; vnc_read = 1; } if (0 != VbNvSetup(&cached_vnc)) return -1; retval = VbNvGet(&cached_vnc, param, &value); if (0 != VbNvTeardown(&cached_vnc)) return -1; if (0 != retval) return -1; /* TODO: If vnc.raw_changed, attempt to reopen NVRAM for write and * save the new defaults. If we're able to, log. */ /* TODO: release lock */ return (int)value; }
static void test_ssync(VbError_t retval, int recovery_reason, const char *desc) { uint32_t u; TEST_EQ(VbEcSoftwareSync(0, &cparams), retval, desc); VbNvGet(VbApiKernelGetVnc(), VBNV_RECOVERY_REQUEST, &u); TEST_EQ(u, recovery_reason, " recovery reason"); }
/* Test display key checking */ static void DisplayKeyTest(void) { uint32_t u; ResetMocks(); VbCheckDisplayKey(&cparams, 'q', &vnc); TEST_EQ(*debug_info, '\0', "DisplayKey q = does nothing"); VbApiKernelFree(&cparams); ResetMocks(); VbCheckDisplayKey(&cparams, '\t', &vnc); TEST_NEQ(*debug_info, '\0', "DisplayKey tab = display"); VbApiKernelFree(&cparams); /* Toggle localization */ ResetMocks(); VbNvSet(&vnc, VBNV_LOCALIZATION_INDEX, 0); VbNvTeardown(&vnc); VbCheckDisplayKey(&cparams, VB_KEY_DOWN, &vnc); VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); TEST_EQ(u, 2, "DisplayKey up"); VbCheckDisplayKey(&cparams, VB_KEY_LEFT, &vnc); VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); TEST_EQ(u, 1, "DisplayKey left"); VbCheckDisplayKey(&cparams, VB_KEY_RIGHT, &vnc); VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); TEST_EQ(u, 2, "DisplayKey right"); VbCheckDisplayKey(&cparams, VB_KEY_UP, &vnc); VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); TEST_EQ(u, 0, "DisplayKey up"); VbApiKernelFree(&cparams); /* Reset localization if localization count is invalid */ ResetMocks(); VbNvSet(&vnc, VBNV_LOCALIZATION_INDEX, 1); VbNvTeardown(&vnc); bhdr->signature[0] ^= 0x5a; VbCheckDisplayKey(&cparams, VB_KEY_UP, &vnc); VbNvGet(&vnc, VBNV_LOCALIZATION_INDEX, &u); TEST_EQ(u, 0, "DisplayKey invalid"); VbApiKernelFree(&cparams); }
VbError_t VbCheckDisplayKey(VbCommonParams *cparams, uint32_t key, VbNvContext *vncptr) { int i; /* Update key buffer */ for(i = 1; i < MAGIC_WORD_LEN; i++) MagicBuffer[i - 1] = MagicBuffer[i]; /* Save as lower-case ASCII */ MagicBuffer[MAGIC_WORD_LEN - 1] = (key | 0x20) & 0xFF; if ('\t' == key) { /* Tab = display debug info */ return VbDisplayDebugInfo(cparams, vncptr); } else if (VB_KEY_LEFT == key || VB_KEY_RIGHT == key || VB_KEY_DOWN == key || VB_KEY_UP == key) { /* Arrow keys = change localization */ uint32_t loc = 0; uint32_t count = 0; VbNvGet(vncptr, VBNV_LOCALIZATION_INDEX, &loc); if (VBERROR_SUCCESS != VbGetLocalizationCount(cparams, &count)) loc = 0; /* No localization count (bad GBB?) */ else if (VB_KEY_RIGHT == key || VB_KEY_UP == key) loc = (loc < count - 1 ? loc + 1 : 0); else loc = (loc > 0 ? loc - 1 : count - 1); VBDEBUG(("VbCheckDisplayKey() - change localization to %d\n", (int)loc)); VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, loc); #ifdef SAVE_LOCALE_IMMEDIATELY VbNvTeardown(vncptr); /* really only computes checksum */ if (vncptr->raw_changed) VbExNvStorageWrite(vncptr->raw); #endif /* Force redraw of current screen */ return VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); } if (0 == Memcmp(MagicBuffer, MAGIC_WORD, MAGIC_WORD_LEN)) { if (VBEASTEREGG) (void)VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); } return VBERROR_SUCCESS; }
int SaveNvToBackup(VbNvContext *vnc) { VbNvContext bvnc; uint32_t value; int i; VBDEBUG(("TPM: %s()\n", __func__)); /* Read it first. No point in writing the same data. */ if (TPM_SUCCESS != RollbackBackupRead(bvnc.raw)) return 1; VbNvSetup(&bvnc); VBDEBUG(("TPM: existing backup is %s\n", bvnc.regenerate_crc ? "bad" : "good")); for (i = 0; i < ARRAY_SIZE(backup_params); i++) { VbNvGet(vnc, backup_params[i], &value); VbNvSet(&bvnc, backup_params[i], value); } VbNvTeardown(&bvnc); if (!bvnc.raw_changed) { VBDEBUG(("TPM: Nothing's changed, not writing backup\n")); /* Clear the request flag, since we're happy. */ VbNvSet(vnc, VBNV_BACKUP_NVRAM_REQUEST, 0); return 0; } if (TPM_SUCCESS == RollbackBackupWrite(bvnc.raw)) { /* Clear the request flag if we wrote successfully too */ VbNvSet(vnc, VBNV_BACKUP_NVRAM_REQUEST, 0); return 0; } VBDEBUG(("TPM: Sorry, couldn't write backup.\n")); return 1; }
VbError_t VbInit(VbCommonParams *cparams, VbInitParams *iparams) { VbSharedDataHeader *shared = (VbSharedDataHeader *)cparams->shared_data_blob; GoogleBinaryBlockHeader gbb; VbNvContext vnc; VbError_t retval = VBERROR_SUCCESS; uint32_t recovery = VBNV_RECOVERY_NOT_REQUESTED; int is_s3_resume = 0; uint32_t s3_debug_boot = 0; uint32_t require_official_os = 0; uint32_t tpm_version = 0; uint32_t tpm_status = 0; int has_virt_dev_switch = 0; int is_hw_dev = 0; int is_virt_dev = 0; uint32_t disable_dev_request = 0; uint32_t clear_tpm_owner_request = 0; int is_dev = 0; uint32_t backup_requested = 0; uint32_t backup_for_safety = 0; int lost_nvram; /* Initialize output flags */ iparams->out_flags = 0; retval = VbGbbReadHeader_static(cparams, &gbb); if (retval) return retval; VBDEBUG(("VbInit() input flags 0x%x gbb flags 0x%x\n", iparams->flags, gbb.flags)); /* Set up NV storage */ VbExNvStorageRead(vnc.raw); VbNvSetup(&vnc); lost_nvram = vnc.regenerate_crc; /* Initialize shared data structure */ if (0 != VbSharedDataInit(shared, cparams->shared_data_size)) { VBDEBUG(("Shared data init error\n")); return VBERROR_INIT_SHARED_DATA; } shared->timer_vb_init_enter = VbExGetTimer(); /* Copy some boot switch flags */ /* TODO: in next refactor, just save in/out flags in VbSharedData */ shared->flags = 0; if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED) shared->flags |= VBSD_BOOT_REC_SWITCH_ON; if (iparams->flags & VB_INIT_FLAG_WP_ENABLED) shared->flags |= VBSD_BOOT_FIRMWARE_WP_ENABLED; if (iparams->flags & VB_INIT_FLAG_SW_WP_ENABLED) shared->flags |= VBSD_BOOT_FIRMWARE_SW_WP_ENABLED; if (iparams->flags & VB_INIT_FLAG_S3_RESUME) shared->flags |= VBSD_BOOT_S3_RESUME; if (iparams->flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT) shared->flags |= VBSD_BOOT_RO_NORMAL_SUPPORT; if (iparams->flags & VB_INIT_FLAG_EC_SOFTWARE_SYNC) shared->flags |= VBSD_EC_SOFTWARE_SYNC; if (iparams->flags & VB_INIT_FLAG_EC_SLOW_UPDATE) shared->flags |= VBSD_EC_SLOW_UPDATE; if (iparams->flags & VB_INIT_FLAG_VIRTUAL_REC_SWITCH) shared->flags |= VBSD_BOOT_REC_SWITCH_VIRTUAL; if (iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) shared->flags |= VBSD_OPROM_MATTERS; if (iparams->flags & VB_INIT_FLAG_OPROM_LOADED) shared->flags |= VBSD_OPROM_LOADED; is_s3_resume = (iparams->flags & VB_INIT_FLAG_S3_RESUME ? 1 : 0); /* Check if the OS is requesting a debug S3 reset */ VbNvGet(&vnc, VBNV_DEBUG_RESET_MODE, &s3_debug_boot); if (s3_debug_boot) { if (is_s3_resume) { VBDEBUG(("VbInit() requesting S3 debug boot\n")); iparams->out_flags |= VB_INIT_OUT_S3_DEBUG_BOOT; is_s3_resume = 0; /* Proceed as if normal boot */ } /* * Clear the request even if this is a normal boot, since we * don't want the NEXT S3 resume to be a debug reset unless the * OS asserts the request again. */ VbNvSet(&vnc, VBNV_DEBUG_RESET_MODE, 0); } /* * If this isn't a S3 resume, read the current recovery request, then * clear it so we don't get stuck in recovery mode. */ if (!is_s3_resume) { VbNvGet(&vnc, VBNV_RECOVERY_REQUEST, &recovery); VBDEBUG(("VbInit sees recovery request = %d\n", recovery)); if (VBNV_RECOVERY_NOT_REQUESTED != recovery) VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_NOT_REQUESTED); } /* * If the previous boot failed in the firmware somewhere outside of * verified boot, and recovery is not requested for our own reasons, * request recovery mode. This gives the calling firmware a way to * request recovery if it finds something terribly wrong. */ if (VBNV_RECOVERY_NOT_REQUESTED == recovery && iparams->flags & VB_INIT_FLAG_PREVIOUS_BOOT_FAIL) { recovery = VBNV_RECOVERY_RO_FIRMWARE; } /* * If recovery button is pressed, override recovery reason. Note that * we do this in the S3 resume path also. */ if (iparams->flags & VB_INIT_FLAG_REC_BUTTON_PRESSED) recovery = VBNV_RECOVERY_RO_MANUAL; /* * Copy current recovery reason to shared data. If we fail later on, it * won't matter, since we'll just reboot. */ shared->recovery_reason = (uint8_t)recovery; VBDEBUG(("VbInit now sets shared->recovery_reason = %d\n", recovery)); /* * If this is a S3 resume, resume the TPM. * * FIXME: I think U-Boot won't ever ask us to do this. Can we remove * it? */ if (is_s3_resume) { if (TPM_SUCCESS != RollbackS3Resume()) { /* * If we can't resume, just do a full reboot. No need * to go to recovery mode here, since if the TPM is * really broken we'll catch it on the next boot. */ retval = VBERROR_TPM_S3_RESUME; } } else { /* Should we pay attention to the TPM's virtual dev-switch? */ if (iparams->flags & VB_INIT_FLAG_VIRTUAL_DEV_SWITCH) { shared->flags |= VBSD_HONOR_VIRT_DEV_SWITCH; has_virt_dev_switch = 1; } /* * We always believe the HW dev-switch, since there's one * attached to servo which may be active even on systems * without a physical switch. The EC may also implement a fake * dev-switch for testing. */ if (iparams->flags & VB_INIT_FLAG_DEV_SWITCH_ON) is_hw_dev = 1; /* We may be asked to clear the virtual dev-switch at boot. */ VbNvGet(&vnc, VBNV_DISABLE_DEV_REQUEST, &disable_dev_request); /* Allow GBB flag to override dev switch */ if (gbb.flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) is_hw_dev = 1; /* Have we been explicitly asked to clear the TPM owner? */ VbNvGet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, &clear_tpm_owner_request); /* * Initialize the TPM. If the developer mode state has changed * since the last boot, we need to clear TPM ownership. If the * TPM space is initialized by this call, the virtual * dev-switch will be disabled by default) */ VBDEBUG(("TPM: Call RollbackFirmwareSetup(r%d, d%d)\n", recovery, is_hw_dev)); tpm_status = RollbackFirmwareSetup(is_hw_dev, disable_dev_request, clear_tpm_owner_request, /* two outputs on success */ &is_virt_dev, &tpm_version); if (0 != tpm_status) { VBDEBUG(("Unable to setup TPM and read " "firmware version (0x%x)\n", tpm_status)); if (TPM_E_MUST_REBOOT == tpm_status) { /* * TPM wants to reboot into the same mode we're * in now */ VBDEBUG(("TPM requires a reboot.\n")); if (!recovery) { /* * Not recovery mode. Just reboot (not * into recovery). */ retval = VBERROR_TPM_REBOOT_REQUIRED; goto VbInit_exit; } else if (VBNV_RECOVERY_RO_TPM_REBOOT != shared->recovery_reason) { /* * In recovery mode now, and we haven't * requested a TPM reboot yet, so * request one. */ VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_REBOOT); retval = VBERROR_TPM_REBOOT_REQUIRED; goto VbInit_exit; } } if (!recovery) { VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_S_ERROR); VbNvSet(&vnc, VBNV_RECOVERY_SUBCODE, tpm_status); retval = VBERROR_TPM_FIRMWARE_SETUP; goto VbInit_exit; } } /* TPM setup succeeded, or we're in recovery mode and ignoring * errors. What did we learn? */ shared->fw_version_tpm_start = tpm_version; shared->fw_version_tpm = tpm_version; if (is_hw_dev || (has_virt_dev_switch && is_virt_dev)) { is_dev = 1; shared->flags |= VBSD_BOOT_DEV_SWITCH_ON; } if (disable_dev_request && !is_virt_dev) VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 0); if (clear_tpm_owner_request) { VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_REQUEST, 0); VbNvSet(&vnc, VBNV_CLEAR_TPM_OWNER_DONE, 1); } } /* * If the nvram state was lost, try to restore the bits we care about * from the backup in the TPM. It's okay if we can't, though. * Note: None of the bits that we back up should have been referenced * before this point. Otherwise, they'll just be overwritten here. * All the other bits will be unchanged from whatever has happened to * them since VbNvSetup() reinitialized the VbNvContext. */ if (lost_nvram) RestoreNvFromBackup(&vnc); /* Allow BIOS to load arbitrary option ROMs? */ if (gbb.flags & GBB_FLAG_LOAD_OPTION_ROMS) iparams->out_flags |= VB_INIT_OUT_ENABLE_OPROM; /* Factory may need to boot custom OSes when the dev-switch is on */ if (is_dev && (gbb.flags & GBB_FLAG_ENABLE_ALTERNATE_OS)) iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS; /* Set output flags */ if (VBNV_RECOVERY_NOT_REQUESTED != recovery) { /* Requesting recovery mode */ iparams->out_flags |= (VB_INIT_OUT_ENABLE_RECOVERY | VB_INIT_OUT_CLEAR_RAM | VB_INIT_OUT_ENABLE_DISPLAY | VB_INIT_OUT_ENABLE_USB_STORAGE); } else if (is_dev) { /* Developer switch is on, so need to support dev mode */ iparams->out_flags |= (VB_INIT_OUT_ENABLE_DEVELOPER | VB_INIT_OUT_CLEAR_RAM | VB_INIT_OUT_ENABLE_DISPLAY | VB_INIT_OUT_ENABLE_USB_STORAGE); /* ... which may or may not include custom OSes */ VbNvGet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, &require_official_os); if (!require_official_os) iparams->out_flags |= VB_INIT_OUT_ENABLE_ALTERNATE_OS; /* * Dev-mode needs the VGA option ROM to be loaded so it can * display the scary boot screen. If we don't have it, we need * to request it and reboot so it can be loaded. */ if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) && !(iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) { VbNvSet(&vnc, VBNV_OPROM_NEEDED, 1); /* * If VbInit() is run before Option ROMs are run it * can still respond to the VbNv flag and does not * need to reboot here. */ if (!(iparams->flags & VB_INIT_FLAG_BEFORE_OPROM_LOAD)) retval = VBERROR_VGA_OPROM_MISMATCH; VBDEBUG(("VbInit() needs oprom, doesn't have it\n")); } } else { /* * Normal mode, so disable dev_boot_* flags. This ensures they * will be initially disabled if the user later transitions * back into developer mode. */ VbNvSet(&vnc, VBNV_DEV_BOOT_USB, 0); VbNvSet(&vnc, VBNV_DEV_BOOT_LEGACY, 0); VbNvSet(&vnc, VBNV_DEV_BOOT_SIGNED_ONLY, 0); /* * Back up any changes now, so these values can't be forgotten * by draining the battery. We really only care about these * three fields, but it's uncommon for any others to change so * this is an easier test than checking each one. */ if (vnc.regenerate_crc) backup_for_safety = 1; /* * If we don't need the VGA option ROM but got it anyway, stop * asking for it and reboot in case there's some vulnerability * in using it. */ if ((iparams->flags & VB_INIT_FLAG_OPROM_MATTERS) && (iparams->flags & VB_INIT_FLAG_OPROM_LOADED)) { VbNvSet(&vnc, VBNV_OPROM_NEEDED, 0); /* * If VbInit() is run before Option ROMs are run it * can still respond to the VbNv flag and does not * need to reboot here. */ if (!(iparams->flags & VB_INIT_FLAG_BEFORE_OPROM_LOAD)) retval = VBERROR_VGA_OPROM_MISMATCH; VBDEBUG(("VbInit() has oprom, doesn't need it\n")); } } VbInit_exit: /* * If we successfully backup the NV storage, it will clear the * VBNV_BACKUP_NVRAM_REQUEST field, so we want to do it before * calling VbNvTeardown(). It's okay if we can't backup, though. */ VbNvGet(&vnc, VBNV_BACKUP_NVRAM_REQUEST, &backup_requested); if (backup_requested || backup_for_safety) SaveNvToBackup(&vnc); /* Tear down NV storage */ VbNvTeardown(&vnc); if (vnc.raw_changed) VbExNvStorageWrite(vnc.raw); VBDEBUG(("VbInit() output flags 0x%x\n", iparams->out_flags)); shared->timer_vb_init_exit = VbExGetTimer(); VBDEBUG(("VbInit() returning 0x%x\n", retval)); return retval; }
int do_cros_bootstub(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int status = LOAD_FIRMWARE_RECOVERY; firmware_storage_t file; VbNvContext nvcxt; uint64_t boot_flags = 0; uint32_t recovery_request = 0; uint32_t reason = VBNV_RECOVERY_NOT_REQUESTED; uint8_t *firmware_data; if (firmware_storage_init(&file)) { /* FIXME(clchiou) Bring up a sad face as boot has failed */ VBDEBUG(PREFIX "init_firmware_storage fail\n"); while (1); } if (is_firmware_write_protect_gpio_asserted()) WARN_ON_FAILURE(file.lock_device(file.context)); clear_kernel_shared_data(); /* Fill in the RO firmware ID */ KernelSharedDataType *sd = get_kernel_shared_data(); if (firmware_storage_read(&file, (off_t)CONFIG_OFFSET_RO_FRID, (size_t)CONFIG_LENGTH_RO_FRID, sd->frid)) { VBDEBUG(PREFIX "fail to read fwid\n"); reason = VBNV_RECOVERY_US_UNSPECIFIED; goto RECOVERY; } if (read_nvcontext(&nvcxt) || VbNvGet(&nvcxt, VBNV_RECOVERY_REQUEST, &recovery_request)) { VBDEBUG(PREFIX "fail to read nvcontext\n"); reason = VBNV_RECOVERY_US_UNSPECIFIED; goto RECOVERY; } /* clear VBNV_DEBUG_RESET_MODE after read */ if (VbNvSet(&nvcxt, VBNV_DEBUG_RESET_MODE, 0)) { VBDEBUG(PREFIX "fail to write nvcontext\n"); reason = VBNV_RECOVERY_US_UNSPECIFIED; goto RECOVERY; } if (recovery_request != VBNV_RECOVERY_NOT_REQUESTED) { VBDEBUG(PREFIX "boot recovery cookie set\n"); reason = recovery_request; goto RECOVERY; } if (is_recovery_mode_gpio_asserted()) { VBDEBUG(PREFIX "recovery button pressed\n"); reason = VBNV_RECOVERY_RO_MANUAL; goto RECOVERY; } if (is_developer_mode_gpio_asserted()) boot_flags |= BOOT_FLAG_DEVELOPER; status = load_firmware_wrapper(&file, boot_flags, &nvcxt, NULL, &firmware_data); if (nvcxt.raw_changed && write_nvcontext(&nvcxt)) { VBDEBUG(PREFIX "fail to write nvcontext\n"); reason = VBNV_RECOVERY_US_UNSPECIFIED; goto RECOVERY; } if (status == LOAD_FIRMWARE_SUCCESS) { jump_to_firmware((void (*)(void)) firmware_data); } else if (status == LOAD_FIRMWARE_REBOOT) { cold_reboot(); } /* assert(status == LOAD_FIRMWARE_RECOVERY) */ RECOVERY: VBDEBUG(PREFIX "write to recovery cookie\n"); /* * Although writing back VbNvContext cookies may fail, we boot * recovery firmware anyway. In this way, the recovery reason * would be incorrect, but this is much better than not booting * anything. */ if (reason != VBNV_RECOVERY_NOT_REQUESTED && VbNvSet(&nvcxt, VBNV_RECOVERY_REQUEST, reason)) { /* FIXME: bring up a sad face? */ VBDEBUG(PREFIX "error: cannot write recovery reason\n"); } if (VbNvTeardown(&nvcxt)) { /* FIXME: bring up a sad face? */ VBDEBUG(PREFIX "error: cannot tear down cookie\n"); } if (nvcxt.raw_changed && write_nvcontext(&nvcxt)) { /* FIXME: bring up a sad face? */ VBDEBUG(PREFIX "error: cannot write recovery cookie\n"); } VBDEBUG(PREFIX "jump to recovery firmware and never return\n"); firmware_data = malloc(CONFIG_LENGTH_RECOVERY); WARN_ON_FAILURE(load_recovery_firmware(&file, firmware_data)); jump_to_firmware((void (*)(void)) firmware_data); /* never reach here */ return 1; }
VbError_t VbDisplayDebugInfo(VbCommonParams *cparams, VbNvContext *vncptr) { VbSharedDataHeader *shared = (VbSharedDataHeader *)cparams->shared_data_blob; GoogleBinaryBlockHeader *gbb = cparams->gbb; char buf[DEBUG_INFO_SIZE] = ""; char sha1sum[SHA1_DIGEST_SIZE * 2 + 1]; char hwid[256]; uint32_t used = 0; VbPublicKey *key; VbError_t ret; uint32_t i; /* Redisplay current screen to overwrite any previous debug output */ VbDisplayScreen(cparams, disp_current_screen, 1, vncptr); /* Add hardware ID */ VbRegionReadHWID(cparams, hwid, sizeof(hwid)); used += StrnAppend(buf + used, "HWID: ", DEBUG_INFO_SIZE - used); used += StrnAppend(buf + used, hwid, DEBUG_INFO_SIZE - used); /* Add recovery reason */ used += StrnAppend(buf + used, "\nrecovery_reason: 0x", DEBUG_INFO_SIZE - used); used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, shared->recovery_reason, 16, 2); used += StrnAppend(buf + used, " ", DEBUG_INFO_SIZE - used); used += StrnAppend(buf + used, RecoveryReasonString(shared->recovery_reason), DEBUG_INFO_SIZE - used); /* Add VbSharedData flags */ used += StrnAppend(buf + used, "\nVbSD.flags: 0x", DEBUG_INFO_SIZE - used); used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, shared->flags, 16, 8); /* Add raw contents of VbNvStorage */ used += StrnAppend(buf + used, "\nVbNv.raw:", DEBUG_INFO_SIZE - used); for (i = 0; i < VBNV_BLOCK_SIZE; i++) { used += StrnAppend(buf + used, " ", DEBUG_INFO_SIZE - used); used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, vncptr->raw[i], 16, 2); } /* Add dev_boot_usb flag */ VbNvGet(vncptr, VBNV_DEV_BOOT_USB, &i); used += StrnAppend(buf + used, "\ndev_boot_usb: ", DEBUG_INFO_SIZE - used); used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); /* Add dev_boot_legacy flag */ VbNvGet(vncptr, VBNV_DEV_BOOT_LEGACY, &i); used += StrnAppend(buf + used, "\ndev_boot_legacy: ", DEBUG_INFO_SIZE - used); used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); /* Add dev_boot_signed_only flag */ VbNvGet(vncptr, VBNV_DEV_BOOT_SIGNED_ONLY, &i); used += StrnAppend(buf + used, "\ndev_boot_signed_only: ", DEBUG_INFO_SIZE - used); used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, i, 10, 0); /* Add TPM versions */ used += StrnAppend(buf + used, "\nTPM: fwver=0x", DEBUG_INFO_SIZE - used); used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, shared->fw_version_tpm, 16, 8); used += StrnAppend(buf + used, " kernver=0x", DEBUG_INFO_SIZE - used); used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, shared->kernel_version_tpm, 16, 8); /* Add GBB flags */ used += StrnAppend(buf + used, "\ngbb.flags: 0x", DEBUG_INFO_SIZE - used); if (gbb->major_version == GBB_MAJOR_VER && gbb->minor_version >= 1) { used += Uint64ToString(buf + used, DEBUG_INFO_SIZE - used, gbb->flags, 16, 8); } else { used += StrnAppend(buf + used, "0 (default)", DEBUG_INFO_SIZE - used); } /* Add sha1sum for Root & Recovery keys */ ret = VbGbbReadRootKey(cparams, &key); if (!ret) { FillInSha1Sum(sha1sum, key); VbExFree(key); used += StrnAppend(buf + used, "\ngbb.rootkey: ", DEBUG_INFO_SIZE - used); used += StrnAppend(buf + used, sha1sum, DEBUG_INFO_SIZE - used); } ret = VbGbbReadRecoveryKey(cparams, &key); if (!ret) { FillInSha1Sum(sha1sum, key); VbExFree(key); used += StrnAppend(buf + used, "\ngbb.recovery_key: ", DEBUG_INFO_SIZE - used); used += StrnAppend(buf + used, sha1sum, DEBUG_INFO_SIZE - used); } /* If we're in dev-mode, show the kernel subkey that we expect, too. */ if (0 == shared->recovery_reason) { FillInSha1Sum(sha1sum, &shared->kernel_subkey); used += StrnAppend(buf + used, "\nkernel_subkey: ", DEBUG_INFO_SIZE - used); used += StrnAppend(buf + used, sha1sum, DEBUG_INFO_SIZE - used); } /* Make sure we finish with a newline */ used += StrnAppend(buf + used, "\n", DEBUG_INFO_SIZE - used); /* TODO: add more interesting data: * - Information on current disks */ buf[DEBUG_INFO_SIZE - 1] = '\0'; return VbExDisplayDebugInfo(buf); }
VbError_t VbDisplayScreenFromGBB(VbCommonParams *cparams, uint32_t screen, VbNvContext *vncptr) { char *fullimage = NULL; BmpBlockHeader hdr; uint32_t screen_index; uint32_t localization = 0; VbError_t retval = VBERROR_UNKNOWN; /* Assume error until proven ok */ uint32_t inoutsize; uint32_t i; VbFont_t *font; const char *text_to_show; int rtol = 0; VbError_t ret; ret = VbGbbReadBmpHeader(cparams, &hdr); if (ret) return ret; /* * Translate screen ID into index. Note that not all screens are in * the GBB. * * TODO: ensure screen IDs match indices? Having this translation here * is awful. */ switch (screen) { case VB_SCREEN_DEVELOPER_WARNING: screen_index = SCREEN_DEVELOPER_WARNING; break; case VB_SCREEN_RECOVERY_REMOVE: screen_index = SCREEN_RECOVERY_REMOVE; break; case VB_SCREEN_RECOVERY_NO_GOOD: screen_index = SCREEN_RECOVERY_NO_GOOD; break; case VB_SCREEN_RECOVERY_INSERT: screen_index = SCREEN_RECOVERY_INSERT; break; case VB_SCREEN_RECOVERY_TO_DEV: screen_index = SCREEN_RECOVERY_TO_DEV; break; case VB_SCREEN_DEVELOPER_TO_NORM: screen_index = SCREEN_DEVELOPER_TO_NORM; break; case VB_SCREEN_WAIT: screen_index = SCREEN_WAIT; break; case VB_SCREEN_TO_NORM_CONFIRMED: screen_index = SCREEN_TO_NORM_CONFIRMED; break; case VB_SCREEN_BLANK: case VB_SCREEN_DEVELOPER_EGG: default: /* Screens which aren't in the GBB */ VBDEBUG(("VbDisplayScreenFromGBB(): screen %d not in the GBB\n", (int)screen)); retval = VBERROR_INVALID_SCREEN_INDEX; goto VbDisplayScreenFromGBB_exit; } if (screen_index >= hdr.number_of_screenlayouts) { VBDEBUG(("VbDisplayScreenFromGBB(): " "screen %d index %d not in the GBB\n", (int)screen, (int)screen_index)); retval = VBERROR_INVALID_SCREEN_INDEX; goto VbDisplayScreenFromGBB_exit; } /* Clip localization to number of localizations present in the GBB */ VbNvGet(vncptr, VBNV_LOCALIZATION_INDEX, &localization); if (localization >= hdr.number_of_localizations) { localization = 0; VbNvSet(vncptr, VBNV_LOCALIZATION_INDEX, localization); } /* Display all bitmaps for the image */ for (i = 0; i < MAX_IMAGE_IN_LAYOUT; i++) { ScreenLayout layout; ImageInfo image_info; char hwid[256]; ret = VbGbbReadImage(cparams, localization, screen_index, i, &layout, &image_info, &fullimage, &inoutsize); if (ret == VBERROR_NO_IMAGE_PRESENT) { continue; } else if (ret) { retval = ret; goto VbDisplayScreenFromGBB_exit; } switch(image_info.format) { case FORMAT_BMP: if (i == 0) { /** * In current version GBB bitmaps, first image * is always the background. */ ret = VbExDisplaySetDimension( image_info.width, image_info.height); if (ret) { VBDEBUG(("VbExDisplaySetDimension" "(%d,%d): failed (%#x).\n", image_info.width, image_info.height, ret)); } } retval = VbExDisplayImage(layout.images[i].x, layout.images[i].y, fullimage, inoutsize); break; case FORMAT_FONT: /* * The uncompressed blob is our font structure. Cache * it as needed. */ font = VbInternalizeFontData( (FontArrayHeader *)fullimage); /* TODO: handle text in general here */ if (TAG_HWID == image_info.tag || TAG_HWID_RTOL == image_info.tag) { VbRegionReadHWID(cparams, hwid, sizeof(hwid)); text_to_show = hwid; rtol = (TAG_HWID_RTOL == image_info.tag); } else { text_to_show = ""; rtol = 0; } VbRenderTextAtPos(text_to_show, rtol, layout.images[i].x, layout.images[i].y, font); VbDoneWithFontForNow(font); break; default: VBDEBUG(("VbDisplayScreenFromGBB(): " "unsupported ImageFormat %d\n", image_info.format)); retval = VBERROR_INVALID_GBB; } VbExFree(fullimage); if (VBERROR_SUCCESS != retval) goto VbDisplayScreenFromGBB_exit; } /* Successful if all bitmaps displayed */ retval = VBERROR_SUCCESS; VbRegionCheckVersion(cparams); VbDisplayScreenFromGBB_exit: VBDEBUG(("leaving VbDisplayScreenFromGBB() with %d\n",retval)); return retval; }
static void VbNvStorageTest(void) { VbNvField* vnf; VbNvContext c; uint8_t goodcrc; uint32_t data; memset(&c, 0xA6, sizeof(c)); /* Open with invalid data should set defaults */ TEST_EQ(VbNvSetup(&c), 0, "VbNvSetup()"); TEST_EQ(c.raw[0], 0x70, "VbNvSetup() reset header byte"); /* Close then regenerates the CRC */ TEST_EQ(VbNvTeardown(&c), 0, "VbNvTeardown()"); TEST_NEQ(c.raw[15], 0, "VbNvTeardown() CRC"); TEST_EQ(c.raw_changed, 1, "VbNvTeardown() changed"); goodcrc = c.raw[15]; /* Another open-close pair should not cause further changes */ VbNvSetup(&c); VbNvTeardown(&c); TEST_EQ(c.raw_changed, 0, "VbNvTeardown() didn't change"); TEST_EQ(c.raw[15], goodcrc, "VbNvTeardown() CRC same"); /* Perturbing the header should force defaults */ c.raw[0] ^= 0x40; VbNvSetup(&c); TEST_EQ(c.raw[0], 0x70, "VbNvSetup() reset header byte again"); /* Close then regenerates the CRC */ VbNvTeardown(&c); TEST_EQ(c.raw_changed, 1, "VbNvTeardown() changed again"); TEST_EQ(c.raw[15], goodcrc, "VbNvTeardown() CRC same again"); /* So should perturbing some other byte */ TEST_EQ(c.raw[11], 0, "Kernel byte starts at 0"); c.raw[11] = 12; VbNvSetup(&c); TEST_EQ(c.raw[11], 0, "VbNvSetup() reset kernel byte"); /* Close then regenerates the CRC */ VbNvTeardown(&c); TEST_EQ(c.raw_changed, 1, "VbNvTeardown() changed again"); TEST_EQ(c.raw[15], goodcrc, "VbNvTeardown() CRC same again"); /* Clear the kernel and firmware flags */ VbNvSetup(&c); TEST_EQ(VbNvGet(&c, VBNV_FIRMWARE_SETTINGS_RESET, &data), 0, "Get firmware settings reset"); TEST_EQ(data, 1, "Firmware settings are reset"); TEST_EQ(VbNvSet(&c, VBNV_FIRMWARE_SETTINGS_RESET, 0), 0, "Clear firmware settings reset"); VbNvGet(&c, VBNV_FIRMWARE_SETTINGS_RESET, &data); TEST_EQ(data, 0, "Firmware settings are clear"); TEST_EQ(VbNvGet(&c, VBNV_KERNEL_SETTINGS_RESET, &data), 0, "Get kernel settings reset"); TEST_EQ(data, 1, "Kernel settings are reset"); TEST_EQ(VbNvSet(&c, VBNV_KERNEL_SETTINGS_RESET, 0), 0, "Clear kernel settings reset"); VbNvGet(&c, VBNV_KERNEL_SETTINGS_RESET, &data); TEST_EQ(data, 0, "Kernel settings are clear"); TEST_EQ(c.raw[0], 0x40, "Header byte now just has the header bit"); VbNvTeardown(&c); /* That should have changed the CRC */ TEST_NEQ(c.raw[15], goodcrc, "VbNvTeardown() CRC changed due to flags clear"); /* Test explicitly setting the reset flags again */ VbNvSetup(&c); VbNvSet(&c, VBNV_FIRMWARE_SETTINGS_RESET, 1); VbNvGet(&c, VBNV_FIRMWARE_SETTINGS_RESET, &data); TEST_EQ(data, 1, "Firmware settings forced reset"); VbNvSet(&c, VBNV_FIRMWARE_SETTINGS_RESET, 0); VbNvSet(&c, VBNV_KERNEL_SETTINGS_RESET, 1); VbNvGet(&c, VBNV_KERNEL_SETTINGS_RESET, &data); TEST_EQ(data, 1, "Kernel settings forced reset"); VbNvSet(&c, VBNV_KERNEL_SETTINGS_RESET, 0); VbNvTeardown(&c); /* Get/set an invalid field */ VbNvSetup(&c); TEST_EQ(VbNvGet(&c, -1, &data), 1, "Get invalid setting"); TEST_EQ(VbNvSet(&c, -1, 0), 1, "Set invalid setting"); VbNvTeardown(&c); /* Test other fields */ VbNvSetup(&c); for (vnf = nvfields; vnf->desc; vnf++) { TEST_EQ(VbNvGet(&c, vnf->param, &data), 0, vnf->desc); TEST_EQ(data, vnf->default_value, vnf->desc); TEST_EQ(VbNvSet(&c, vnf->param, vnf->test_value), 0, vnf->desc); TEST_EQ(VbNvGet(&c, vnf->param, &data), 0, vnf->desc); TEST_EQ(data, vnf->test_value, vnf->desc); TEST_EQ(VbNvSet(&c, vnf->param, vnf->test_value2), 0, vnf->desc); TEST_EQ(VbNvGet(&c, vnf->param, &data), 0, vnf->desc); TEST_EQ(data, vnf->test_value2, vnf->desc); } VbNvTeardown(&c); /* None of those changes should have caused a reset to defaults */ VbNvSetup(&c); VbNvGet(&c, VBNV_FIRMWARE_SETTINGS_RESET, &data); TEST_EQ(data, 0, "Firmware settings are still clear"); VbNvGet(&c, VBNV_KERNEL_SETTINGS_RESET, &data); TEST_EQ(data, 0, "Kernel settings are still clear"); VbNvTeardown(&c); /* Verify writing identical settings doesn't cause the CRC to regenerate */ VbNvSetup(&c); TEST_EQ(c.regenerate_crc, 0, "No regen CRC on open"); for (vnf = nvfields; vnf->desc; vnf++) TEST_EQ(VbNvSet(&c, vnf->param, vnf->test_value2), 0, vnf->desc); TEST_EQ(c.regenerate_crc, 0, "No regen CRC if data not changed"); VbNvTeardown(&c); TEST_EQ(c.raw_changed, 0, "No raw change if data not changed"); /* Test out-of-range fields mapping to defaults */ VbNvSetup(&c); VbNvSet(&c, VBNV_TRY_B_COUNT, 16); VbNvGet(&c, VBNV_TRY_B_COUNT, &data); TEST_EQ(data, 15, "Try b count out of range"); VbNvSet(&c, VBNV_RECOVERY_REQUEST, 0x101); VbNvGet(&c, VBNV_RECOVERY_REQUEST, &data); TEST_EQ(data, VBNV_RECOVERY_LEGACY, "Recovery request out of range"); VbNvSet(&c, VBNV_LOCALIZATION_INDEX, 0x102); VbNvGet(&c, VBNV_LOCALIZATION_INDEX, &data); TEST_EQ(data, 0, "Localization index out of range"); VbNvTeardown(&c); }
VbError_t VbBootDeveloper(VbCommonParams *cparams, LoadKernelParams *p) { GoogleBinaryBlockHeader *gbb = cparams->gbb; VbSharedDataHeader *shared = (VbSharedDataHeader *)cparams->shared_data_blob; uint32_t allow_usb = 0, allow_legacy = 0, ctrl_d_pressed = 0; VbAudioContext *audio = 0; VBDEBUG(("Entering %s()\n", __func__)); /* Check if USB booting is allowed */ VbNvGet(&vnc, VBNV_DEV_BOOT_USB, &allow_usb); VbNvGet(&vnc, VBNV_DEV_BOOT_LEGACY, &allow_legacy); /* Handle GBB flag override */ if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_USB) allow_usb = 1; if (gbb->flags & GBB_FLAG_FORCE_DEV_BOOT_LEGACY) allow_legacy = 1; /* Show the dev mode warning screen */ VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc); /* Get audio/delay context */ audio = VbAudioOpen(cparams); /* We'll loop until we finish the delay or are interrupted */ do { uint32_t key; if (VbWantShutdown(gbb->flags)) { VBDEBUG(("VbBootDeveloper() - shutdown requested!\n")); VbAudioClose(audio); return VBERROR_SHUTDOWN_REQUESTED; } key = VbExKeyboardRead(); switch (key) { case 0: /* nothing pressed */ break; case '\r': /* Only disable virtual dev switch if allowed by GBB */ if (!(gbb->flags & GBB_FLAG_ENTER_TRIGGERS_TONORM)) break; case ' ': /* See if we should disable virtual dev-mode switch. */ VBDEBUG(("%s shared->flags=0x%x\n", __func__, shared->flags)); if (shared->flags & VBSD_HONOR_VIRT_DEV_SWITCH && shared->flags & VBSD_BOOT_DEV_SWITCH_ON) { /* Stop the countdown while we go ask... */ VbAudioClose(audio); if (gbb->flags & GBB_FLAG_FORCE_DEV_SWITCH_ON) { /* * TONORM won't work (only for * non-shipping devices). */ VBDEBUG(("%s() - TONORM rejected by " "FORCE_DEV_SWITCH_ON\n", __func__)); VbExDisplayDebugInfo( "WARNING: TONORM prohibited by " "GBB FORCE_DEV_SWITCH_ON.\n\n"); VbExBeep(120, 400); break; } VbDisplayScreen(cparams, VB_SCREEN_DEVELOPER_TO_NORM, 0, &vnc); /* Ignore space in VbUserConfirms()... */ switch (VbUserConfirms(cparams, 0)) { case 1: VBDEBUG(("%s() - leaving dev-mode.\n", __func__)); VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 1); VbDisplayScreen( cparams, VB_SCREEN_TO_NORM_CONFIRMED, 0, &vnc); VbExSleepMs(5000); return VBERROR_TPM_REBOOT_REQUIRED; case -1: VBDEBUG(("%s() - shutdown requested\n", __func__)); return VBERROR_SHUTDOWN_REQUESTED; default: /* Stay in dev-mode */ VBDEBUG(("%s() - stay in dev-mode\n", __func__)); VbDisplayScreen( cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc); /* Start new countdown */ audio = VbAudioOpen(cparams); } } else { /* * No virtual dev-mode switch, so go directly * to recovery mode. */ VBDEBUG(("%s() - going to recovery\n", __func__)); VbSetRecoveryRequest( VBNV_RECOVERY_RW_DEV_SCREEN); VbAudioClose(audio); return VBERROR_LOAD_KERNEL_RECOVERY; } break; case 0x04: /* Ctrl+D = dismiss warning; advance to timeout */ VBDEBUG(("VbBootDeveloper() - " "user pressed Ctrl+D; skip delay\n")); ctrl_d_pressed = 1; goto fallout; break; case 0x0c: VBDEBUG(("VbBootDeveloper() - " "user pressed Ctrl+L; Try legacy boot\n")); VbTryLegacy(allow_legacy); break; case VB_KEY_CTRL_ENTER: /* * The Ctrl-Enter is special for Lumpy test purpose; * fall through to Ctrl+U handler. */ case 0x15: /* Ctrl+U = try USB boot, or beep if failure */ VBDEBUG(("VbBootDeveloper() - " "user pressed Ctrl+U; try USB\n")); if (!allow_usb) { VBDEBUG(("VbBootDeveloper() - " "USB booting is disabled\n")); VbExDisplayDebugInfo( "WARNING: Booting from external media " "(USB/SD) has not been enabled. Refer " "to the developer-mode documentation " "for details.\n"); VbExBeep(120, 400); VbExSleepMs(120); VbExBeep(120, 400); } else { /* * Clear the screen to show we get the Ctrl+U * key press. */ VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc); if (VBERROR_SUCCESS == VbTryLoadKernel(cparams, p, VB_DISK_FLAG_REMOVABLE)) { VBDEBUG(("VbBootDeveloper() - " "booting USB\n")); VbAudioClose(audio); return VBERROR_SUCCESS; } else { VBDEBUG(("VbBootDeveloper() - " "no kernel found on USB\n")); VbExBeep(250, 200); VbExSleepMs(120); /* * Clear recovery requests from failed * kernel loading, so that powering off * at this point doesn't put us into * recovery mode. */ VbSetRecoveryRequest( VBNV_RECOVERY_NOT_REQUESTED); /* Show dev mode warning screen again */ VbDisplayScreen( cparams, VB_SCREEN_DEVELOPER_WARNING, 0, &vnc); } } break; default: VBDEBUG(("VbBootDeveloper() - pressed key %d\n", key)); VbCheckDisplayKey(cparams, key, &vnc); break; } } while(VbAudioLooping(audio)); fallout: /* If defaulting to legacy boot, try that unless Ctrl+D was pressed */ if ((gbb->flags & GBB_FLAG_DEFAULT_DEV_BOOT_LEGACY) && !ctrl_d_pressed) { VBDEBUG(("VbBootDeveloper() - defaulting to legacy\n")); VbTryLegacy(allow_legacy); } /* Timeout or Ctrl+D; attempt loading from fixed disk */ VBDEBUG(("VbBootDeveloper() - trying fixed disk\n")); VbAudioClose(audio); return VbTryLoadKernel(cparams, p, VB_DISK_FLAG_FIXED); }