Example #1
0
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");
}
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;
}