diff options
author | Aaron Durbin <adurbin@chromium.org> | 2015-05-01 16:48:54 -0500 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2015-05-11 22:39:22 +0200 |
commit | 17200ad5fb6a6eb065ca108d308cd1a94268966e (patch) | |
tree | cb5cc6e9540dfc2057dc818f1e97a7be82def302 | |
parent | 01562b6cb567dca5440b65250da2580db52b31e8 (diff) |
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)
-rw-r--r-- | src/include/program_loading.h | 1 | ||||
-rw-r--r-- | src/lib/Makefile.inc | 1 | ||||
-rw-r--r-- | src/lib/loaders/Makefile.inc | 2 | ||||
-rw-r--r-- | src/lib/loaders/load_and_run_payload.c | 4 | ||||
-rw-r--r-- | src/lib/loaders/load_and_run_ramstage.c | 4 | ||||
-rw-r--r-- | src/lib/loaders/load_and_run_romstage.c | 7 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot2/Makefile.inc | 9 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot2/common.c | 33 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot2/misc.h | 3 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot2/vboot_handoff.c | 53 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot2/vboot_loader.c | 244 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot2/verstub.c | 107 |
12 files changed, 280 insertions, 188 deletions
diff --git a/src/include/program_loading.h b/src/include/program_loading.h index ca80b0e793..5fc0fd604a 100644 --- a/src/include/program_loading.h +++ b/src/include/program_loading.h @@ -39,6 +39,7 @@ struct buffer_area { }; enum prog_type { + PROG_VERSTAGE, PROG_ROMSTAGE, PROG_RAMSTAGE, PROG_PAYLOAD, diff --git a/src/lib/Makefile.inc b/src/lib/Makefile.inc index 1d0851f667..db83698a04 100644 --- a/src/lib/Makefile.inc +++ b/src/lib/Makefile.inc @@ -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 diff --git a/src/lib/loaders/Makefile.inc b/src/lib/loaders/Makefile.inc index fbc7f870a4..9dca06bcfa 100644 --- a/src/lib/loaders/Makefile.inc +++ b/src/lib/loaders/Makefile.inc @@ -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 diff --git a/src/lib/loaders/load_and_run_payload.c b/src/lib/loaders/load_and_run_payload.c index 7a08097884..4928e0c1e0 100644 --- a/src/lib/loaders/load_and_run_payload.c +++ b/src/lib/loaders/load_and_run_payload.c @@ -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, }; diff --git a/src/lib/loaders/load_and_run_ramstage.c b/src/lib/loaders/load_and_run_ramstage.c index fddea4d55e..504dbff55c 100644 --- a/src/lib/loaders/load_and_run_ramstage.c +++ b/src/lib/loaders/load_and_run_ramstage.c @@ -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, }; diff --git a/src/lib/loaders/load_and_run_romstage.c b/src/lib/loaders/load_and_run_romstage.c index b54bfc7e5c..b54c6917ac 100644 --- a/src/lib/loaders/load_and_run_romstage.c +++ b/src/lib/loaders/load_and_run_romstage.c @@ -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) diff --git a/src/vendorcode/google/chromeos/vboot2/Makefile.inc b/src/vendorcode/google/chromeos/vboot2/Makefile.inc index fe1d237931..7a2ea24643 100644 --- a/src/vendorcode/google/chromeos/vboot2/Makefile.inc +++ b/src/vendorcode/google/chromeos/vboot2/Makefile.inc @@ -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) diff --git a/src/vendorcode/google/chromeos/vboot2/common.c b/src/vendorcode/google/chromeos/vboot2/common.c index d70a108b05..beffebeba1 100644 --- a/src/vendorcode/google/chromeos/vboot2/common.c +++ b/src/vendorcode/google/chromeos/vboot2/common.c @@ -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; diff --git a/src/vendorcode/google/chromeos/vboot2/misc.h b/src/vendorcode/google/chromeos/vboot2/misc.h index 7425082064..d942d5649b 100644 --- a/src/vendorcode/google/chromeos/vboot2/misc.h +++ b/src/vendorcode/google/chromeos/vboot2/misc.h @@ -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, diff --git a/src/vendorcode/google/chromeos/vboot2/vboot_handoff.c b/src/vendorcode/google/chromeos/vboot2/vboot_handoff.c index b84e47ef16..16261b4a02 100644 --- a/src/vendorcode/google/chromeos/vboot2/vboot_handoff.c +++ b/src/vendorcode/google/chromeos/vboot2/vboot_handoff.c @@ -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; - - 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); + 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"); + + /* 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; } } diff --git a/src/vendorcode/google/chromeos/vboot2/vboot_loader.c b/src/vendorcode/google/chromeos/vboot2/vboot_loader.c new file mode 100644 index 0000000000..929f0cbb2b --- /dev/null +++ b/src/vendorcode/google/chromeos/vboot2/vboot_loader.c @@ -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, +}; diff --git a/src/vendorcode/google/chromeos/vboot2/verstub.c b/src/vendorcode/google/chromeos/vboot2/verstub.c deleted file mode 100644 index c4319ba256..0000000000 --- a/src/vendorcode/google/chromeos/vboot2/verstub.c +++ /dev/null @@ -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; -} |