aboutsummaryrefslogtreecommitdiff
path: root/src/vendorcode/google/chromeos/vboot2/vboot_loader.c
diff options
context:
space:
mode:
authorAaron Durbin <adurbin@chromium.org>2015-05-01 16:48:54 -0500
committerPatrick Georgi <pgeorgi@google.com>2015-05-11 22:39:22 +0200
commit17200ad5fb6a6eb065ca108d308cd1a94268966e (patch)
treecb5cc6e9540dfc2057dc818f1e97a7be82def302 /src/vendorcode/google/chromeos/vboot2/vboot_loader.c
parent01562b6cb567dca5440b65250da2580db52b31e8 (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)
Diffstat (limited to 'src/vendorcode/google/chromeos/vboot2/vboot_loader.c')
-rw-r--r--src/vendorcode/google/chromeos/vboot2/vboot_loader.c244
1 files changed, 244 insertions, 0 deletions
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,
+};