vboot: inject vboot loader for stage loading

As previously done the vboot loader can be optionally
inserted in the stage loading logic in order to
decide the source of each stage. This current patch
allows for verstage to be loaded and interrogated
for the source of all subsequent stages. Additionally,
it's also possible to build this logic directly into
one of the additional stages.

Note that this patch does not allow x86 to work.

Change-Id: Iece018f01b220720c2803dc73c60b2c080d637d0
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/10154
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Tested-by: build bot (Jenkins)
This commit is contained in:
Aaron Durbin 2015-05-01 16:48:54 -05:00 committed by Patrick Georgi
parent 01562b6cb5
commit 17200ad5fb
12 changed files with 279 additions and 187 deletions

View File

@ -39,6 +39,7 @@ struct buffer_area {
};
enum prog_type {
PROG_VERSTAGE,
PROG_ROMSTAGE,
PROG_RAMSTAGE,
PROG_PAYLOAD,

View File

@ -34,6 +34,7 @@ verstage-y += prog_ops.c
verstage-y += delay.c
verstage-y += cbfs.c
verstage-y += cbfs_core.c
verstage-y += halt.c
verstage-y += memcmp.c
verstage-$(CONFIG_COLLECT_TIMESTAMPS) += timestamp.c
verstage-$(CONFIG_CONSOLE_CBMEM) += cbmem_console.c

View File

@ -23,3 +23,5 @@ romstage-y += cbfs_ramstage_loader.c
romstage-y += load_and_run_ramstage.c
ramstage-y += cbfs_payload_loader.c
ramstage-y += load_and_run_payload.c
verstage-y += cbfs_romstage_loader.c
verstage-y += load_and_run_romstage.c

View File

@ -27,12 +27,12 @@
#include <symbols.h>
#include <timestamp.h>
extern const struct prog_loader_ops vboot_payload_loader;
extern const struct prog_loader_ops vboot_loader;
extern const struct prog_loader_ops cbfs_payload_loader;
static const struct prog_loader_ops *payload_ops[] = {
#if CONFIG_VBOOT_VERIFY_FIRMWARE
&vboot_payload_loader,
&vboot_loader,
#endif
&cbfs_payload_loader,
};

View File

@ -27,11 +27,11 @@
#include <timestamp.h>
extern const struct prog_loader_ops cbfs_ramstage_loader;
extern const struct prog_loader_ops vboot_ramstage_loader;
extern const struct prog_loader_ops vboot_loader;
static const struct prog_loader_ops *loaders[] = {
#if CONFIG_VBOOT_VERIFY_FIRMWARE
&vboot_ramstage_loader,
&vboot_loader,
#endif
&cbfs_ramstage_loader,
};

View File

@ -24,12 +24,19 @@
#include <cbfs.h>
#include <halt.h>
#include <program_loading.h>
#include <rules.h>
#include <timestamp.h>
extern const struct prog_loader_ops cbfs_romstage_loader;
extern const struct prog_loader_ops vboot_loader;
static const struct prog_loader_ops *loaders[] = {
#if CONFIG_VBOOT_VERIFY_FIRMWARE
&vboot_loader,
#endif
#if !ENV_VERSTAGE || (ENV_VERSTAGE && !CONFIG_RETURN_FROM_VERSTAGE)
&cbfs_romstage_loader,
#endif
};
void run_romstage(void)

View File

@ -20,13 +20,16 @@
libverstage-generic-ccopts += -D__PRE_RAM__ -D__VERSTAGE__
verstage-generic-ccopts += -D__PRE_RAM__ -D__VERSTAGE__
bootblock-y += vboot_loader.c
romstage-y += vboot_loader.c
ramstage-y += vboot_loader.c
verstage-y += vboot_loader.c
bootblock-y += ../vboot_common.c
verstage-y += ../vboot_common.c
romstage-y += ../vboot_common.c
ramstage-y += ../vboot_common.c
bootblock-y += verstub.c
libverstage-y += verstub.c
bootblock-y += common.c
libverstage-y += verstage.c
verstage-y += common.c
@ -37,6 +40,8 @@ libverstage-y += antirollback.c
endif
romstage-y += vboot_handoff.c common.c
ramstage-y += common.c
verstage-y += verstage.ld
ifeq ($(CONFIG_SEPARATE_VERSTAGE),y)

View File

@ -25,39 +25,6 @@
#include "../vboot_handoff.h"
#include "misc.h"
void *vboot_load_stage(int stage_index,
struct vboot_region *fw_main,
struct vboot_components *fw_info)
{
struct cbfs_media default_media, *media = &default_media;
uintptr_t fc_addr;
uint32_t fc_size;
void *entry;
if (stage_index >= fw_info->num_components) {
printk(BIOS_INFO, "invalid stage index\n");
return NULL;
}
fc_addr = fw_main->offset_addr + fw_info->entries[stage_index].offset;
fc_size = fw_info->entries[stage_index].size;
if (fc_size == 0 ||
fc_addr + fc_size > fw_main->offset_addr + fw_main->size) {
printk(BIOS_INFO, "invalid stage address or size\n");
return NULL;
}
init_default_cbfs_media(media);
/* we're making cbfs access offset outside of the region managed by
* cbfs. this works because cbfs_load_stage_by_offset does not check
* the offset. */
entry = cbfs_load_stage_by_offset(media, fc_addr);
if (entry == (void *)-1)
entry = NULL;
return entry;
}
struct vb2_working_data * const vboot_get_working_data(void)
{
return (struct vb2_working_data *)_vboot2_work;

View File

@ -22,8 +22,7 @@
#include "../vboot_common.h"
void *vboot2_verify_firmware(void);
void *vboot2_load_ramstage(void);
void vboot_fill_handoff(void);
void verstage_main(void);
void *vboot_load_stage(int stage_index,
struct vboot_region *fw_main,

View File

@ -36,30 +36,6 @@
#include "../vboot_handoff.h"
#include "misc.h"
static void *load_ramstage(struct vboot_handoff *vboot_handoff,
struct vboot_region *fw_main)
{
struct vboot_components *fw_info;
void *ret;
int i;
fw_info = vboot_locate_components(fw_main);
if (fw_info == NULL)
die("failed to locate firmware components\n");
/* these offset & size are used to load a rw boot loader */
for (i = 0; i < fw_info->num_components; i++) {
vboot_handoff->components[i].address =
fw_main->offset_addr + fw_info->entries[i].offset;
vboot_handoff->components[i].size = fw_info->entries[i].size;
}
timestamp_add_now(TS_START_COPYRAM);
ret = vboot_load_stage(CONFIG_VBOOT_RAMSTAGE_INDEX, fw_main, fw_info);
timestamp_add_now(TS_END_COPYRAM);
return ret;
}
/**
* Sets vboot_handoff based on the information in vb2_shared_data
*
@ -146,14 +122,13 @@ static void fill_vboot_handoff(struct vboot_handoff *vboot_handoff,
vb_sd->recovery_reason = vb2_sd->recovery_reason;
}
/**
* Load ramstage and return the entry point
*/
void *vboot2_load_ramstage(void)
void vboot_fill_handoff(void)
{
int i;
struct vboot_handoff *vh;
struct vb2_shared_data *sd;
struct vboot_region fw_main;
struct vboot_components *fw_info;
struct vb2_working_data *wd = vboot_get_working_data();
sd = vboot_get_work_buffer(wd);
@ -172,17 +147,15 @@ void *vboot2_load_ramstage(void)
/* needed until we finish transtion to vboot2 for kernel verification */
fill_vboot_handoff(vh, sd);
if (vboot_is_readonly_path(wd))
/* we're on recovery path. continue to ro-ramstage. */
return NULL;
vb2_get_selected_region(wd, &fw_main);
fw_info = vboot_locate_components(&fw_main);
if (fw_info == NULL)
die("failed to locate firmware components\n");
if (IS_ENABLED(CONFIG_MULTIPLE_CBFS_INSTANCES)) {
return cbfs_load_stage(CBFS_DEFAULT_MEDIA,
CONFIG_CBFS_PREFIX "/ramstage");
} else {
printk(BIOS_INFO, "loading ramstage from Slot %c\n",
sd->fw_slot ? 'B' : 'A');
vb2_get_selected_region(wd, &fw_main);
return load_ramstage(vh, &fw_main);
/* these offset & size are used to load a rw boot loader */
for (i = 0; i < fw_info->num_components; i++) {
vh->components[i].address =
fw_main.offset_addr + fw_info->entries[i].offset;
vh->components[i].size = fw_info->entries[i].size;
}
}

View File

@ -0,0 +1,244 @@
/*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc.
*/
#include <cbfs.h>
#include <console/console.h>
#include <program_loading.h>
#include <rules.h>
#include <string.h>
#include "misc.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 void init_vb2_working_data(void)
{
struct vb2_working_data *wd;
wd = vboot_get_working_data();
memset(wd, 0, _vboot2_work_size);
/*
* vboot prefers 16-byte alignment. This takes away 16 bytes
* from the VBOOT2_WORK region, but the vboot devs said that's okay.
*/
wd->buffer_offset = ALIGN_UP(sizeof(*wd), 16);
wd->buffer_size = _vboot2_work_size - wd->buffer_offset;
}
static int vboot_loader_active(struct prog *prog)
{
struct vb2_working_data *wd;
int run_verification;
run_verification = verification_should_run();
if (run_verification) {
init_vb2_working_data();
verstage_main();
} else if (verstage_should_load()) {
struct prog verstage = {
.type = PROG_VERSTAGE,
.name = CONFIG_CBFS_PREFIX "/verstage",
};
/* load verstage from RO */
if (cbfs_load_prog_stage(CBFS_DEFAULT_MEDIA, &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 handoff structure before moving to ramstage so all
* downstream users have access to vboot results. */
if (ENV_ROMSTAGE)
vboot_fill_handoff();
wd = vboot_get_working_data();
if (vboot_is_slot_selected(wd)) {
if (IS_ENABLED(CONFIG_MULTIPLE_CBFS_INSTANCES) &&
run_verification) {
/* RW A or B */
struct vboot_region fw_main;
vb2_get_selected_region(wd, &fw_main);
cbfs_set_header_offset(fw_main.offset_addr);
}
return 1;
}
return 0;
}
static uintptr_t vboot_fw_region(int fw_index, struct vboot_region *fw_main,
struct vboot_components *fw_info, size_t *size)
{
uintptr_t fc_addr;
uint32_t fc_size;
if (fw_index >= fw_info->num_components) {
printk(BIOS_INFO, "invalid stage index: %d\n", fw_index);
return 0;
}
fc_addr = fw_main->offset_addr + fw_info->entries[fw_index].offset;
fc_size = fw_info->entries[fw_index].size;
if (fc_size == 0 ||
fc_addr + fc_size > fw_main->offset_addr + fw_main->size) {
printk(BIOS_INFO, "invalid stage address or size\n");
return 0;
}
*size = fc_size;
return fc_addr;
}
/* This function is only called when vboot_loader_active() returns 1. That
* means we are taking vboot paths. */
static int vboot_prepare(struct prog *prog)
{
struct vb2_working_data *wd;
struct vboot_region fw_main;
struct vboot_components *fw_info;
/* 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;
/* In the multi cbfs case the cbfs offset pointer has already been
* updated after firmware verification. */
if (IS_ENABLED(CONFIG_MULTIPLE_CBFS_INSTANCES)) {
if (!ENV_RAMSTAGE &&
cbfs_load_prog_stage(CBFS_DEFAULT_MEDIA, prog) != 0)
return -1;
/* Need to load payload. */
if (ENV_RAMSTAGE) {
void *payload;
size_t size;
payload = cbfs_get_file_content(CBFS_DEFAULT_MEDIA,
prog->name,
CBFS_TYPE_PAYLOAD,
&size);
if (payload == NULL)
die("Couldn't load payload\n");
prog_set_area(prog, payload, size);
}
return 0;
}
wd = vboot_get_working_data();
vb2_get_selected_region(wd, &fw_main);
fw_info = vboot_locate_components(&fw_main);
if (fw_info == NULL)
die("failed to locate firmware components\n");
/* Load payload in ramstage. */
if (ENV_RAMSTAGE) {
uintptr_t payload;
void *payload_ptr;
size_t size;
payload = vboot_fw_region(CONFIG_VBOOT_BOOT_LOADER_INDEX,
&fw_main, fw_info, &size);
if (payload == 0)
die("Couldn't load payload.");
payload_ptr = vboot_get_region(payload, size, NULL);
if (payload_ptr == NULL)
die("Couldn't load payload.");
prog_set_area(prog, payload_ptr, size);
} else {
uintptr_t stage;
size_t size;
int stage_index = 0;
if (prog->type == PROG_ROMSTAGE)
stage_index = CONFIG_VBOOT_ROMSTAGE_INDEX;
else if (prog->type == PROG_RAMSTAGE)
stage_index = CONFIG_VBOOT_RAMSTAGE_INDEX;
else
die("Invalid program type for vboot.");
stage = vboot_fw_region(stage_index, &fw_main, fw_info, &size);
if (stage == 0)
die("Vboot stage load failed.");
if (cbfs_load_prog_stage_by_offset(CBFS_DEFAULT_MEDIA,
prog, stage) < 0)
die("Vboot couldn't load stage");
}
return 0;
}
const struct prog_loader_ops vboot_loader = {
.name = "VBOOT",
.is_loader_active = vboot_loader_active,
.prepare = vboot_prepare,
};

View File

@ -1,107 +0,0 @@
/*
* This file is part of the coreboot project.
*
* Copyright 2014 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <arch/stages.h>
#include <cbfs.h>
#include <console/console.h>
#include <string.h>
#include <timestamp.h>
#include "../chromeos.h"
#include "../symbols.h"
#include "misc.h"
static struct vb2_working_data *init_vb2_working_data(void)
{
struct vb2_working_data *wd;
wd = vboot_get_working_data();
memset(wd, 0, _vboot2_work_size);
/*
* vboot prefers 16-byte alignment. This takes away 16 bytes
* from the VBOOT2_WORK region, but the vboot devs said that's okay.
*/
wd->buffer_offset = ALIGN_UP(sizeof(*wd), 16);
wd->buffer_size = _vboot2_work_size - wd->buffer_offset;
return wd;
}
/**
* Verify a slot and jump to the next stage
*
* This could be either part of the (1) bootblock or the (2) verstage, depending
* on CONFIG_RETURN_FROM_VERSTAGE.
*
* 1) It jumps to the verstage and comes back, then, loads the romstage over the
* verstage space and exits to it. (note the cbfs cache is trashed on return
* from the verstage.)
*
* 2) We're already in the verstage. Verify firmware, then load the romstage and
* exits to it.
*/
void *vboot2_verify_firmware(void)
{
void *entry;
struct vb2_working_data *wd;
wd = init_vb2_working_data();
if (IS_ENABLED(CONFIG_RETURN_FROM_VERSTAGE)) {
/* load verstage from RO */
entry = cbfs_load_stage(CBFS_DEFAULT_MEDIA,
CONFIG_CBFS_PREFIX "/verstage");
if (entry == (void *)-1)
die("failed to load verstage");
/* verify and select a slot */
stage_exit(entry);
} else {
verstage_main();
}
/* jump to the selected slot */
timestamp_add_now(TS_START_COPYROM);
entry = (void *)-1;
if (vboot_is_slot_selected(wd)) {
/* RW A or B */
struct vboot_region fw_main;
vb2_get_selected_region(wd, &fw_main);
if (IS_ENABLED(CONFIG_MULTIPLE_CBFS_INSTANCES)) {
cbfs_set_header_offset(fw_main.offset_addr);
entry = cbfs_load_stage(CBFS_DEFAULT_MEDIA,
CONFIG_CBFS_PREFIX "/romstage");
} else {
struct vboot_components *fw_info;
fw_info = vboot_locate_components(&fw_main);
if (fw_info == NULL)
die("failed to locate firmware components\n");
entry = vboot_load_stage(CONFIG_VBOOT_ROMSTAGE_INDEX,
&fw_main, fw_info);
}
} else if (vboot_is_readonly_path(wd)) {
/* RO */
entry = cbfs_load_stage(CBFS_DEFAULT_MEDIA,
CONFIG_CBFS_PREFIX "/romstage");
}
timestamp_add_now(TS_END_COPYROM);
return entry;
}