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; }
static void enable_graphics(void) { if (!CONFIG_OPROM_MATTERS) return; int oprom_loaded = flag_fetch(FLAG_OPROM); // Manipulating vboot's internal data and calling its internal // functions is NOT NICE and will give you athlete's foot and make // you unpopular at parties. Right now it's the only way to ensure // graphics are enabled, though, so it's a necessary evil. if (!oprom_loaded) { printf("Enabling graphics.\n"); VbNvContext context; VbExNvStorageRead(context.raw); VbNvSetup(&context); VbNvSet(&context, VBNV_OPROM_NEEDED, 1); VbNvTeardown(&context); VbExNvStorageWrite(context.raw); printf("Rebooting.\n"); if (cold_reboot()) halt(); } }
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; }
int VbSetNvStorage(VbNvParam param, int value) { VbNvContext vnc; int retval = -1; int i; if (0 != VbReadNvStorage(&vnc)) return -1; if (0 != VbNvSetup(&vnc)) goto VbSetNvCleanup; i = VbNvSet(&vnc, param, (uint32_t)value); if (0 != VbNvTeardown(&vnc)) goto VbSetNvCleanup; if (0 != i) goto VbSetNvCleanup; if (vnc.raw_changed) { vnc_read = 0; if (0 != VbWriteNvStorage(&vnc)) goto VbSetNvCleanup; } /* Success */ retval = 0; VbSetNvCleanup: /* TODO: release lock */ return retval; }
/* Reset mock data (for use before each test) */ static void ResetMocks(void) { Memset(&cparams, 0, sizeof(cparams)); cparams.shared_data_size = sizeof(shared_data); cparams.shared_data_blob = shared_data; cparams.gbb_data = &gbb; Memset(&gbb, 0, sizeof(gbb)); gbb.major_version = GBB_MAJOR_VER; gbb.minor_version = GBB_MINOR_VER; gbb.flags = 0; /* * Only the outermost vboot_api_kernel call sets vboot_api_kernel's * vnc. So clear it here too. */ Memset(VbApiKernelGetVnc(), 0, sizeof(VbNvContext)); VbNvSetup(VbApiKernelGetVnc()); VbNvTeardown(VbApiKernelGetVnc()); /* So CRC gets generated */ Memset(&shared_data, 0, sizeof(shared_data)); VbSharedDataInit(shared, sizeof(shared_data)); trust_ec = 0; mock_in_rw = 0; ec_ro_protected = 0; ec_rw_protected = 0; ec_run_image = 0; /* 0 = RO, 1 = RW */ ec_ro_updated = 0; ec_rw_updated = 0; in_rw_retval = VBERROR_SUCCESS; protect_retval = VBERROR_SUCCESS; update_retval = VBERROR_SUCCESS; run_retval = VBERROR_SUCCESS; get_expected_retval = VBERROR_SUCCESS; shutdown_request_calls_left = -1; Memset(mock_ec_ro_hash, 0, sizeof(mock_ec_ro_hash)); mock_ec_ro_hash[0] = 42; mock_ec_ro_hash_size = sizeof(mock_ec_ro_hash); Memset(mock_ec_rw_hash, 0, sizeof(mock_ec_rw_hash)); mock_ec_rw_hash[0] = 42; mock_ec_rw_hash_size = sizeof(mock_ec_rw_hash); Memset(want_ec_hash, 0, sizeof(want_ec_hash)); want_ec_hash[0] = 42; want_ec_hash_size = sizeof(want_ec_hash); update_hash = 42; Memset(mock_sha, 0, sizeof(want_ec_hash)); mock_sha[0] = 42; // TODO: ensure these are actually needed Memset(screens_displayed, 0, sizeof(screens_displayed)); screens_count = 0; }
/* Reset mock data (for use before each test) */ static void ResetMocks(void) { int gbb_used; Memset(gbb_data, 0, sizeof(gbb_data)); gbb->major_version = GBB_MAJOR_VER; gbb->minor_version = GBB_MINOR_VER; gbb->flags = 0; gbb_used = sizeof(GoogleBinaryBlockHeader); gbb->hwid_offset = gbb_used; strcpy(gbb_data + gbb->hwid_offset, "Test HWID"); gbb->hwid_size = strlen(gbb_data + gbb->hwid_offset) + 1; gbb_used = (gbb_used + gbb->hwid_size + 7) & ~7; gbb->bmpfv_offset = gbb_used; bhdr = (BmpBlockHeader *)(gbb_data + gbb->bmpfv_offset); gbb->bmpfv_size = sizeof(BmpBlockHeader); gbb_used = (gbb_used + gbb->bmpfv_size + 7) & ~7; memcpy(bhdr->signature, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE); bhdr->major_version = BMPBLOCK_MAJOR_VERSION; bhdr->minor_version = BMPBLOCK_MINOR_VERSION; bhdr->number_of_localizations = 3; Memset(&cparams, 0, sizeof(cparams)); cparams.shared_data_size = sizeof(shared_data); cparams.shared_data_blob = shared_data; cparams.gbb_data = gbb; cparams.gbb_size = sizeof(gbb_data); /* * Note, VbApiKernelFree() expects this to be allocated by * VbExMalloc(), so we cannot just assign it staticly. */ cparams.gbb = VbExMalloc(sizeof(*gbb)); gbb->header_size = sizeof(*gbb); gbb->rootkey_offset = gbb_used; gbb->rootkey_size = 64; gbb_used += 64; gbb->recovery_key_offset = gbb_used; gbb->recovery_key_size = 64; gbb_used += 64; memcpy(cparams.gbb, gbb, sizeof(*gbb)); Memset(&vnc, 0, sizeof(vnc)); VbNvSetup(&vnc); VbNvTeardown(&vnc); /* So CRC gets generated */ Memset(&shared_data, 0, sizeof(shared_data)); VbSharedDataInit(shared, sizeof(shared_data)); *debug_info = 0; }
VbError_t VbLockDevice(void) { VbExNvStorageRead(vnc.raw); VbNvSetup(&vnc); VBDEBUG(("%s() - Storing request to leave dev-mode.\n", __func__)); VbNvSet(&vnc, VBNV_DISABLE_DEV_REQUEST, 1); VbNvTeardown(&vnc); if (vnc.raw_changed) VbExNvStorageWrite(vnc.raw); VBDEBUG(("%s() Mode change will take effect on next reboot.\n", __func__)); 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; }
/* Main routine */ int main(int argc, char* argv[]) { const char* image_name; uint64_t key_size; uint8_t* key_blob = NULL; VbSharedDataHeader* shared; GoogleBinaryBlockHeader* gbb; VbError_t rv; int c, argsleft; int errorcnt = 0; char *e = 0; Memset(&lkp, 0, sizeof(LoadKernelParams)); lkp.bytes_per_lba = LBA_BYTES; lkp.boot_flags = BOOT_FLAG_RECOVERY; Memset(&vnc, 0, sizeof(VbNvContext)); VbNvSetup(&vnc); lkp.nv_context = &vnc; Memset(&cparams, 0, sizeof(VbCommonParams)); /* Parse options */ opterr = 0; while ((c=getopt(argc, argv, ":b:")) != -1) { switch (c) { case 'b': lkp.boot_flags = strtoull(optarg, &e, 0); if (!*optarg || (e && *e)) { fprintf(stderr, "Invalid argument to -%c: \"%s\"\n", c, optarg); errorcnt++; } break; case '?': fprintf(stderr, "Unrecognized switch: -%c\n", optopt); errorcnt++; break; case ':': fprintf(stderr, "Missing argument to -%c\n", optopt); errorcnt++; break; default: errorcnt++; break; } } /* Update argc */ argsleft = argc - optind; if (errorcnt || !argsleft) { fprintf(stderr, "usage: %s [options] <drive_image> [<sign_key>]\n", argv[0]); fprintf(stderr, "\noptions:\n"); /* These cases are because uint64_t isn't necessarily the same as ULL. */ fprintf(stderr, " -b NUM boot flag bits (default %" PRIu64 "):\n", (uint64_t)BOOT_FLAG_RECOVERY); fprintf(stderr, " %" PRIu64 " = developer mode on\n", (uint64_t)BOOT_FLAG_DEVELOPER); fprintf(stderr, " %" PRIu64 " = recovery mode on\n", (uint64_t)BOOT_FLAG_RECOVERY); return 1; } image_name = argv[optind]; /* Read header signing key blob */ if (argsleft > 1) { key_blob = ReadFile(argv[optind+1], &key_size); if (!key_blob) { fprintf(stderr, "Unable to read key file %s\n", argv[optind+1]); return 1; } printf("Read %" PRIu64 " bytes of key from %s\n", key_size, argv[optind+1]); } /* Initialize the GBB */ lkp.gbb_size = sizeof(GoogleBinaryBlockHeader) + key_size; lkp.gbb_data = (void*)malloc(lkp.gbb_size); gbb = (GoogleBinaryBlockHeader*)lkp.gbb_data; cparams.gbb = gbb; Memset(gbb, 0, lkp.gbb_size); Memcpy(gbb->signature, GBB_SIGNATURE, GBB_SIGNATURE_SIZE); gbb->major_version = GBB_MAJOR_VER; gbb->minor_version = GBB_MINOR_VER; gbb->header_size = sizeof(GoogleBinaryBlockHeader); /* Fill in the given key, if any, for both root and recovery */ if (key_blob) { gbb->rootkey_offset = gbb->header_size; gbb->rootkey_size = key_size; Memcpy((uint8_t*)gbb + gbb->rootkey_offset, key_blob, key_size); gbb->recovery_key_offset = gbb->rootkey_offset; gbb->recovery_key_size = key_size; } /* Initialize the shared data area */ lkp.shared_data_blob = malloc(VB_SHARED_DATA_REC_SIZE); lkp.shared_data_size = VB_SHARED_DATA_REC_SIZE; shared = (VbSharedDataHeader*)lkp.shared_data_blob; if (0 != VbSharedDataInit(shared, lkp.shared_data_size)) { fprintf(stderr, "Unable to init shared data\n"); return 1; } /* Copy in the key blob, if any */ if (key_blob) { if (0 != VbSharedDataSetKernelKey(shared, (VbPublicKey*)key_blob)) { fprintf(stderr, "Unable to set key in shared data\n"); return 1; } } /* Free the key blob, now that we're done with it */ free(key_blob); printf("bootflags = %" PRIu64 "\n", lkp.boot_flags); /* Get image size */ printf("Reading from image: %s\n", image_name); image_file = fopen(image_name, "rb"); if (!image_file) { fprintf(stderr, "Unable to open image file %s\n", image_name); return 1; } fseek(image_file, 0, SEEK_END); lkp.streaming_lba_count = (ftell(image_file) / LBA_BYTES); lkp.gpt_lba_count = lkp.streaming_lba_count; rewind(image_file); printf("Streaming LBA count: %" PRIu64 "\n", lkp.streaming_lba_count); /* Allocate a buffer for the kernel */ lkp.kernel_buffer = malloc(KERNEL_BUFFER_SIZE); if(!lkp.kernel_buffer) { fprintf(stderr, "Unable to allocate kernel buffer.\n"); return 1; } lkp.kernel_buffer_size = KERNEL_BUFFER_SIZE; /* Call LoadKernel() */ rv = LoadKernel(&lkp, &cparams); printf("LoadKernel() returned %d\n", rv); if (VBERROR_SUCCESS == rv) { printf("Partition number: %" PRIu64 "\n", lkp.partition_number); printf("Bootloader address: %" PRIu64 "\n", lkp.bootloader_address); printf("Bootloader size: %" PRIu64 "\n", lkp.bootloader_size); printf("Partition guid: " "%02x%02x%02x%02x-%02x%02x-%02x%02x" "-%02x%02x-%02x%02x%02x%02x%02x%02x\n", lkp.partition_guid[3], lkp.partition_guid[2], lkp.partition_guid[1], lkp.partition_guid[0], lkp.partition_guid[5], lkp.partition_guid[4], lkp.partition_guid[7], lkp.partition_guid[6], lkp.partition_guid[8], lkp.partition_guid[9], lkp.partition_guid[10], lkp.partition_guid[11], lkp.partition_guid[12], lkp.partition_guid[13], lkp.partition_guid[14], lkp.partition_guid[15]); } fclose(image_file); free(lkp.kernel_buffer); return rv != VBERROR_SUCCESS; }
/* Reset mock data (for use before each test) */ static void ResetMocks(void) { GoogleBinaryBlockHeader *gbb; BmpBlockHeader *bhdr; ImageInfo *image_info; ScreenLayout *layout; int gbb_used; Memset(&vnc, 0, sizeof(vnc)); VbNvSetup(&vnc); VbNvTeardown(&vnc); /* So CRC gets generated */ Memset(&cparams, 0, sizeof(cparams)); cparams.shared_data_size = sizeof(shared_data); cparams.shared_data_blob = shared_data; Memset(&fparams, 0, sizeof(fparams)); Memset(gbb_data, 0, sizeof(gbb_data)); gbb = (GoogleBinaryBlockHeader *)gbb_data; gbb->major_version = GBB_MAJOR_VER; gbb->minor_version = GBB_MINOR_VER; gbb->flags = 0; gbb_used = sizeof(GoogleBinaryBlockHeader); gbb->hwid_offset = gbb_used; strcpy(gbb_data + gbb->hwid_offset, "Test HWID"); gbb->hwid_size = strlen(gbb_data + gbb->hwid_offset) + 1; gbb_used = (gbb_used + gbb->hwid_size + 7) & ~7; gbb->bmpfv_offset = gbb_used; bhdr = (BmpBlockHeader *)(gbb_data + gbb->bmpfv_offset); gbb->bmpfv_size = sizeof(BmpBlockHeader); gbb_used = (gbb_used + gbb->bmpfv_size + 7) & ~7; memcpy(bhdr->signature, BMPBLOCK_SIGNATURE, BMPBLOCK_SIGNATURE_SIZE); bhdr->major_version = BMPBLOCK_MAJOR_VERSION; bhdr->minor_version = BMPBLOCK_MINOR_VERSION; bhdr->number_of_localizations = 3; bhdr->number_of_screenlayouts = 1; layout = (ScreenLayout *)(gbb_data + gbb_used); gbb_used += sizeof(*layout); layout->images[0].x = 1; layout->images[0].image_info_offset = gbb_used - gbb->bmpfv_offset; /* First image is uncompressed */ image_info = (ImageInfo *)(gbb_data + gbb_used); image_info->format = FORMAT_BMP; image_info->compressed_size = ORIGINAL_SIZE; image_info->original_size = ORIGINAL_SIZE; image_info->compression = COMPRESS_NONE; gbb_used += sizeof(*image_info); strcpy(gbb_data + gbb_used, "original"); gbb_used += ORIGINAL_SIZE; /* Second image is compressed */ layout->images[1].image_info_offset = gbb_used - gbb->bmpfv_offset; layout->images[1].x = 2; image_info = (ImageInfo *)(gbb_data + gbb_used); image_info->format = FORMAT_BMP; image_info->compressed_size = COMPRESSED_SIZE; image_info->original_size = ORIGINAL_SIZE; image_info->compression = COMPRESS_LZMA1; gbb_used += sizeof(*image_info) + COMPRESSED_SIZE; Memset(&shared_data, 0, sizeof(shared_data)); VbSharedDataInit(shared, sizeof(shared_data)); shared->fw_keyblock_flags = 0xABCDE0; mock_tpm_version = mock_lf_tpm_version = 0x20004; shared->fw_version_tpm_start = mock_tpm_version; mock_lf_retval = 0; mock_seen_region = 0; }
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 VbSelectFirmware(VbCommonParams *cparams, VbSelectFirmwareParams *fparams) { VbSharedDataHeader *shared = (VbSharedDataHeader *)cparams->shared_data_blob; VbNvContext vnc; VbError_t retval = VBERROR_UNKNOWN; /* Default to error */ int is_rec = (shared->recovery_reason ? 1 : 0); int is_dev = (shared->flags & VBSD_BOOT_DEV_SWITCH_ON ? 1 : 0); uint32_t tpm_status = 0; /* Start timer */ shared->timer_vb_select_firmware_enter = VbExGetTimer(); /* Load NV storage */ VbExNvStorageRead(vnc.raw); VbNvSetup(&vnc); if (is_rec) { /* * Recovery is requested; go straight to recovery without * checking the RW firmware. */ VBDEBUG(("VbSelectFirmware() detected recovery request\n")); /* Go directly to recovery mode */ fparams->selected_firmware = VB_SELECT_FIRMWARE_RECOVERY; } else { /* Chain to LoadFirmware() */ retval = LoadFirmware(cparams, fparams, &vnc); /* Exit if we failed to find an acceptable firmware */ if (VBERROR_SUCCESS != retval) goto VbSelectFirmware_exit; /* Translate the selected firmware path */ if (shared->flags & VBSD_LF_USE_RO_NORMAL) { /* Request the read-only normal/dev code path */ fparams->selected_firmware = VB_SELECT_FIRMWARE_READONLY; } else if (0 == shared->firmware_index) fparams->selected_firmware = VB_SELECT_FIRMWARE_A; else { fparams->selected_firmware = VB_SELECT_FIRMWARE_B; } /* Update TPM if necessary */ if (shared->fw_version_tpm_start < shared->fw_version_tpm) { tpm_status = RollbackFirmwareWrite(shared->fw_version_tpm); if (0 != tpm_status) { VBDEBUG(("Can't write FW version to TPM.\n")); VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_W_ERROR); retval = VBERROR_TPM_WRITE_FIRMWARE; goto VbSelectFirmware_exit; } } /* Lock firmware versions in TPM */ tpm_status = RollbackFirmwareLock(); if (0 != tpm_status) { VBDEBUG(("Unable to lock firmware version in TPM.\n")); VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_L_ERROR); retval = VBERROR_TPM_LOCK_FIRMWARE; goto VbSelectFirmware_exit; } } /* * At this point, we have a good idea of how we are going to * boot. Update the TPM with this state information. */ tpm_status = SetTPMBootModeState(is_dev, is_rec, shared->fw_keyblock_flags); if (0 != tpm_status) { VBDEBUG(("Can't update the TPM with boot mode information.\n")); if (!is_rec) { VbNvSet(&vnc, VBNV_RECOVERY_REQUEST, VBNV_RECOVERY_RO_TPM_U_ERROR); retval = VBERROR_TPM_SET_BOOT_MODE_STATE; goto VbSelectFirmware_exit; } } /* Success! */ retval = VBERROR_SUCCESS; VbSelectFirmware_exit: /* Save NV storage */ VbNvTeardown(&vnc); if (vnc.raw_changed) VbExNvStorageWrite(vnc.raw); /* Stop timer */ shared->timer_vb_select_firmware_exit = VbExGetTimer(); /* Should always have a known error code */ VbAssert(VBERROR_UNKNOWN != retval); return retval; }
VbError_t VbSelectAndLoadKernel(VbCommonParams *cparams, VbSelectAndLoadKernelParams *kparams) { VbSharedDataHeader *shared = (VbSharedDataHeader *)cparams->shared_data_blob; VbError_t retval = VBERROR_SUCCESS; LoadKernelParams p; uint32_t tpm_status = 0; /* Start timer */ shared->timer_vb_select_and_load_kernel_enter = VbExGetTimer(); VbExNvStorageRead(vnc.raw); VbNvSetup(&vnc); /* Clear output params in case we fail */ kparams->disk_handle = NULL; kparams->partition_number = 0; kparams->bootloader_address = 0; kparams->bootloader_size = 0; kparams->flags = 0; Memset(kparams->partition_guid, 0, sizeof(kparams->partition_guid)); cparams->bmp = NULL; cparams->gbb = VbExMalloc(sizeof(*cparams->gbb)); retval = VbGbbReadHeader_static(cparams, cparams->gbb); if (VBERROR_SUCCESS != retval) goto VbSelectAndLoadKernel_exit; /* Do EC software sync if necessary */ if ((shared->flags & VBSD_EC_SOFTWARE_SYNC) && !(cparams->gbb->flags & GBB_FLAG_DISABLE_EC_SOFTWARE_SYNC)) { int oprom_mismatch = 0; retval = VbEcSoftwareSync(0, cparams); /* Save reboot requested until after possible PD sync */ if (retval == VBERROR_VGA_OPROM_MISMATCH) oprom_mismatch = 1; else if (retval != VBERROR_SUCCESS) goto VbSelectAndLoadKernel_exit; #ifdef PD_SYNC if (!(cparams->gbb->flags & GBB_FLAG_DISABLE_PD_SOFTWARE_SYNC)) { retval = VbEcSoftwareSync(1, cparams); if (retval == VBERROR_VGA_OPROM_MISMATCH) oprom_mismatch = 1; else if (retval != VBERROR_SUCCESS) goto VbSelectAndLoadKernel_exit; } #endif /* Request reboot to unload VGA Option ROM */ if (oprom_mismatch) { retval = VBERROR_VGA_OPROM_MISMATCH; goto VbSelectAndLoadKernel_exit; } } /* Read kernel version from the TPM. Ignore errors in recovery mode. */ tpm_status = RollbackKernelRead(&shared->kernel_version_tpm); if (0 != tpm_status) { VBDEBUG(("Unable to get kernel versions from TPM\n")); if (!shared->recovery_reason) { VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_R_ERROR); retval = VBERROR_TPM_READ_KERNEL; goto VbSelectAndLoadKernel_exit; } } shared->kernel_version_tpm_start = shared->kernel_version_tpm; /* Fill in params for calls to LoadKernel() */ Memset(&p, 0, sizeof(p)); p.shared_data_blob = cparams->shared_data_blob; p.shared_data_size = cparams->shared_data_size; p.gbb_data = cparams->gbb_data; p.gbb_size = cparams->gbb_size; /* * This could be set to NULL, in which case the vboot header * information about the load address and size will be used. */ p.kernel_buffer = kparams->kernel_buffer; p.kernel_buffer_size = kparams->kernel_buffer_size; p.nv_context = &vnc; p.boot_flags = 0; if (shared->flags & VBSD_BOOT_DEV_SWITCH_ON) p.boot_flags |= BOOT_FLAG_DEVELOPER; /* Handle separate normal and developer firmware builds. */ #if defined(VBOOT_FIRMWARE_TYPE_NORMAL) /* Normal-type firmware always acts like the dev switch is off. */ p.boot_flags &= ~BOOT_FLAG_DEVELOPER; #elif defined(VBOOT_FIRMWARE_TYPE_DEVELOPER) /* Developer-type firmware fails if the dev switch is off. */ if (!(p.boot_flags & BOOT_FLAG_DEVELOPER)) { /* * Dev firmware should be signed with a key that only verifies * when the dev switch is on, so we should never get here. */ VBDEBUG(("Developer firmware called with dev switch off!\n")); VbSetRecoveryRequest(VBNV_RECOVERY_RW_DEV_MISMATCH); retval = VBERROR_DEV_FIRMWARE_SWITCH_MISMATCH; goto VbSelectAndLoadKernel_exit; } #else /* * Recovery firmware, or merged normal+developer firmware. No need to * override flags. */ #endif /* Select boot path */ if (shared->recovery_reason) { /* Recovery boot */ p.boot_flags |= BOOT_FLAG_RECOVERY; retval = VbBootRecovery(cparams, &p); VbExEcEnteringMode(0, VB_EC_RECOVERY); VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc); } else if (p.boot_flags & BOOT_FLAG_DEVELOPER) { /* Developer boot */ retval = VbBootDeveloper(cparams, &p); VbExEcEnteringMode(0, VB_EC_DEVELOPER); VbDisplayScreen(cparams, VB_SCREEN_BLANK, 0, &vnc); } else { /* Normal boot */ VbExEcEnteringMode(0, VB_EC_NORMAL); retval = VbBootNormal(cparams, &p); if ((1 == shared->firmware_index) && (shared->flags & VBSD_FWB_TRIED)) { /* * Special cases for when we're trying a new firmware * B. These are needed because firmware updates also * usually change the kernel key, which means that the * B firmware can only boot a new kernel, and the old * firmware in A can only boot the previous kernel. * * Don't advance the TPM if we're trying a new firmware * B, because we don't yet know if the new kernel will * successfully boot. We still want to be able to fall * back to the previous firmware+kernel if the new * firmware+kernel fails. * * If we found only invalid kernels, reboot and try * again. This allows us to fall back to the previous * firmware+kernel instead of giving up and going to * recovery mode right away. We'll still go to * recovery mode if we run out of tries and the old * firmware can't find a kernel it likes. */ if (VBERROR_INVALID_KERNEL_FOUND == retval) { VBDEBUG(("Trying firmware B, " "and only found invalid kernels.\n")); VbSetRecoveryRequest(VBNV_RECOVERY_NOT_REQUESTED); goto VbSelectAndLoadKernel_exit; } } else { /* Not trying a new firmware B. */ /* See if we need to update the TPM. */ VBDEBUG(("Checking if TPM kernel version needs " "advancing\n")); if (shared->kernel_version_tpm > shared->kernel_version_tpm_start) { tpm_status = RollbackKernelWrite( shared->kernel_version_tpm); if (0 != tpm_status) { VBDEBUG(("Error writing kernel " "versions to TPM.\n")); VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_W_ERROR); retval = VBERROR_TPM_WRITE_KERNEL; goto VbSelectAndLoadKernel_exit; } } } } if (VBERROR_SUCCESS != retval) goto VbSelectAndLoadKernel_exit; /* Save disk parameters */ kparams->disk_handle = p.disk_handle; kparams->partition_number = (uint32_t)p.partition_number; kparams->bootloader_address = p.bootloader_address; kparams->bootloader_size = (uint32_t)p.bootloader_size; kparams->flags = p.flags; Memcpy(kparams->partition_guid, p.partition_guid, sizeof(kparams->partition_guid)); /* Lock the kernel versions. Ignore errors in recovery mode. */ tpm_status = RollbackKernelLock(shared->recovery_reason); if (0 != tpm_status) { VBDEBUG(("Error locking kernel versions.\n")); if (!shared->recovery_reason) { VbSetRecoveryRequest(VBNV_RECOVERY_RW_TPM_L_ERROR); retval = VBERROR_TPM_LOCK_KERNEL; goto VbSelectAndLoadKernel_exit; } } VbSelectAndLoadKernel_exit: VbApiKernelFree(cparams); VbNvTeardown(&vnc); if (vnc.raw_changed) VbExNvStorageWrite(vnc.raw); /* Stop timer */ shared->timer_vb_select_and_load_kernel_exit = VbExGetTimer(); kparams->kernel_buffer = p.kernel_buffer; kparams->kernel_buffer_size = p.kernel_buffer_size; VBDEBUG(("VbSelectAndLoadKernel() returning %d\n", (int)retval)); /* Pass through return value from boot path */ return retval; }