blob: b5570dd7cae45c583f82737c29f95df28f2c67bd [file] [log] [blame]
/*
* This file is part of the coreboot project.
*
* Copyright 2015 Google, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <assets.h>
#include <cbfs.h>
#include <cbmem.h>
#include <console/console.h>
#include <rmodule.h>
#include <rules.h>
#include <string.h>
#include "misc.h"
#include "../vboot_handoff.h"
#include "../symbols.h"
/* The stage loading code is compiled and entered from multiple stages. The
* helper functions below attempt to provide more clarity on when certain
* code should be called. */
static int verification_should_run(void)
{
if (ENV_VERSTAGE && IS_ENABLED(CONFIG_SEPARATE_VERSTAGE))
return 1;
if (!IS_ENABLED(CONFIG_SEPARATE_VERSTAGE)) {
if (ENV_ROMSTAGE &&
IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE))
return 1;
if (ENV_BOOTBLOCK &&
IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK))
return 1;
}
return 0;
}
static int verstage_should_load(void)
{
if (!IS_ENABLED(CONFIG_SEPARATE_VERSTAGE))
return 0;
if (ENV_ROMSTAGE && IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE))
return 1;
if (ENV_BOOTBLOCK && IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK))
return 1;
return 0;
}
static int vboot_active(struct asset *asset)
{
int run_verification;
run_verification = verification_should_run();
if (run_verification) {
verstage_main();
} else if (verstage_should_load()) {
struct cbfsf file;
struct prog verstage =
PROG_INIT(ASSET_VERSTAGE,
CONFIG_CBFS_PREFIX "/verstage");
printk(BIOS_DEBUG, "VBOOT: Loading verstage.\n");
/* load verstage from RO */
if (cbfs_boot_locate(&file, prog_name(&verstage), NULL))
die("failed to load verstage");
cbfs_file_data(prog_rdev(&verstage), &file);
if (cbfs_prog_stage_load(&verstage))
die("failed to load verstage");
/* verify and select a slot */
prog_run(&verstage);
/* This is not actually possible to hit this condition at
* runtime, but this provides a hint to the compiler for dead
* code elimination below. */
if (!IS_ENABLED(CONFIG_RETURN_FROM_VERSTAGE))
return 0;
}
/*
* Fill in vboot cbmem objects before moving to ramstage so all
* downstream users have access to vboot results. This path only
* applies to platforms employing VBOOT_DYNAMIC_WORK_BUFFER because
* cbmem comes online prior to vboot verification taking place. For
* other platforms the vboot cbmem objects are initialized when
* cbmem comes online.
*/
if (ENV_ROMSTAGE && IS_ENABLED(CONFIG_VBOOT_DYNAMIC_WORK_BUFFER)) {
vb2_store_selected_region();
vboot_fill_handoff();
}
return vboot_is_slot_selected();
}
static int vboot_locate_by_components(const struct region_device *fw_main,
struct asset *asset)
{
struct vboot_components *fw_info;
size_t metadata_sz;
size_t offset;
size_t size;
struct region_device *fw = asset_rdev(asset);
int fw_index = 0;
if (asset_type(asset) == ASSET_ROMSTAGE)
fw_index = CONFIG_VBOOT_ROMSTAGE_INDEX;
else if (asset_type(asset) == ASSET_RAMSTAGE)
fw_index = CONFIG_VBOOT_RAMSTAGE_INDEX;
else if (asset_type(asset) == ASSET_PAYLOAD)
fw_index = CONFIG_VBOOT_BOOT_LOADER_INDEX;
else if (asset_type(asset) == ASSET_REFCODE)
fw_index = CONFIG_VBOOT_REFCODE_INDEX;
else if (asset_type(asset) == ASSET_BL31)
fw_index = CONFIG_VBOOT_BL31_INDEX;
else
die("Invalid program type for vboot.");
metadata_sz = sizeof(*fw_info);
metadata_sz += MAX_PARSED_FW_COMPONENTS * sizeof(fw_info->entries[0]);
fw_info = rdev_mmap(fw_main, 0, metadata_sz);
if (fw_info == NULL) {
printk(BIOS_INFO, "No component metadata.\n");
return -1;
}
if (fw_index >= fw_info->num_components) {
printk(BIOS_INFO, "invalid index: %d\n", fw_index);
rdev_munmap(fw_main, fw_info);
return -1;
}
offset = fw_info->entries[fw_index].offset;
size = fw_info->entries[fw_index].size;
rdev_munmap(fw_main, fw_info);
if (rdev_chain(fw, fw_main, offset, size)) {
printk(BIOS_INFO, "invalid offset or size\n");
return -1;
}
return 0;
}
static int vboot_locate_by_multi_cbfs(const struct region_device *fw_main,
struct asset *asset)
{
struct cbfsf file;
if (cbfs_locate(&file, fw_main, asset_name(asset), NULL))
return -1;
cbfs_file_data(asset_rdev(asset), &file);
return 0;
}
static int vboot_asset_locate(const struct region_device *fw_main,
struct asset *asset)
{
if (IS_ENABLED(CONFIG_MULTIPLE_CBFS_INSTANCES))
return vboot_locate_by_multi_cbfs(fw_main, asset);
else
return vboot_locate_by_components(fw_main, asset);
}
/* This function is only called when vboot_active() returns 1. That
* means we are taking vboot paths. */
static int vboot_locate(struct asset *asset)
{
struct region_device fw_main;
/* Code size optimization. We'd never actually get called under the
* followin cirumstances because verstage was loaded and ran -- never
* returning. */
if (verstage_should_load() && !IS_ENABLED(CONFIG_RETURN_FROM_VERSTAGE))
return 0;
if (vb2_get_selected_region(&fw_main))
die("failed to reference selected region\n");
return vboot_asset_locate(&fw_main, asset);
}
const struct asset_provider vboot_provider = {
.name = "VBOOT",
.is_active = vboot_active,
.locate = vboot_locate,
};