diff options
author | Aaron Durbin <adurbin@chromium.org> | 2013-03-01 17:12:26 -0600 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2013-03-22 00:15:21 +0100 |
commit | fd79562915bbdea93fc7b37e657856acf808e64f (patch) | |
tree | c3807d0f50354d0efaf3835fdda993f9ffa49714 /src/vendorcode/google/chromeos/vboot_loader.c | |
parent | c0650894f8ca50a7609971418e8eaa4c674f36a9 (diff) |
romstage: add support for vboot firmware selection
This patch implements support for vboot firmware selection. The vboot
support is comprised of the following pieces:
1. vboot_loader.c - this file contains the entry point,
vboot_verify_firmware(), for romstage to call in order to perform
vboot selection. The loader sets up all the data for the wrapper
to use.
2. vboot_wrapper.c - this file contains the implementation calling the vboot
API. It calls VbInit() and VbSelectFirmware() with the data supplied
by the loader.
The vboot wrapper is compiled and linked as an rmodule and placed in
cbfs as 'fallback/vboot'. It's loaded into memory and relocated just
like the way ramstage would be. After being loaded the loader calls into
wrapper. When the wrapper sees that a given piece of firmware has been
selected it parses firmware component information for a predetermined
number of components.
Vboot result information is passed to downstream users by way of the
vboot_handoff structure. This structure lives in cbmem and contains
the shared data, selected firmware, VbInitParams, and parsed firwmare
components.
During ramstage there are only 2 changes:
1. Copy the shared vboot data from vboot_handoff to the chromeos acpi
table.
2. If a firmware selection was made in romstage the boot loader
component is used for the payload.
Noteable Information:
- no vboot path for S3.
- assumes that all RW firmware contains a book keeping header for the
components that comprise the signed firmware area.
- As sanity check there is a limit to the number of firmware components
contained in a signed firmware area. That's so that an errant value
doesn't cause the size calculation to erroneously read memory it
shouldn't.
- RO normal path isn't supported. It's assumed that firmware will always
load the verified RW on all boots but recovery.
- If vboot requests memory to be cleared it is assumed that the boot
loader will take care of that by looking at the out flags in
VbInitParams.
Built and booted. Noted firmware select worked on an image with
RW firmware support. Also checked that recovery mode worked as well
by choosing the RO path.
Change-Id: I45de725c44ee5b766f866692a20881c42ee11fa8
Signed-off-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/2854
Tested-by: build bot (Jenkins)
Reviewed-by: Ronald G. Minnich <rminnich@gmail.com>
Diffstat (limited to 'src/vendorcode/google/chromeos/vboot_loader.c')
-rw-r--r-- | src/vendorcode/google/chromeos/vboot_loader.c | 280 |
1 files changed, 280 insertions, 0 deletions
diff --git a/src/vendorcode/google/chromeos/vboot_loader.c b/src/vendorcode/google/chromeos/vboot_loader.c new file mode 100644 index 0000000000..a1a4586d5c --- /dev/null +++ b/src/vendorcode/google/chromeos/vboot_loader.c @@ -0,0 +1,280 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2013 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 <stdint.h> +#include <stddef.h> +#include <cbfs.h> +#include <cbmem.h> +#include <console/console.h> +#include <console/vtxprintf.h> +#include <pc80/tpm.h> +#include <reset.h> +#include <romstage_handoff.h> +#include <rmodule.h> +#include <string.h> +#include <stdlib.h> +#include <timestamp.h> +#include "chromeos.h" +#include "fmap.h" +#include "vboot_context.h" +#include "vboot_handoff.h" + +static void vboot_run_stub(struct vboot_context *context) +{ + const struct cbmem_entry *vboot_entry; + struct rmodule vbootstub; + struct cbfs_stage *stage; + size_t region_size; + int rmodule_offset; + int load_offset; + char *vboot_region; + void (*entry)(struct vboot_context *context); + + stage = cbfs_find_file(CONFIG_CBFS_PREFIX "/vboot", CBFS_TYPE_STAGE); + + if (stage == NULL) + return; + + rmodule_offset = + rmodule_calc_region(DYN_CBMEM_ALIGN_SIZE, + stage->memlen, ®ion_size, &load_offset); + + vboot_entry = cbmem_entry_add(0xffffffff, region_size); + + if (vboot_entry == NULL) { + printk(BIOS_DEBUG, "Couldn't get region for vboot stub.\n"); + return; + } + + vboot_region = cbmem_entry_start(vboot_entry); + + if (cbfs_decompress(stage->compression, &stage[1], + &vboot_region[rmodule_offset], stage->len)) { + printk(BIOS_DEBUG, "Couldn't decompress vboot stub.\n"); + goto out; + } + + if (rmodule_parse(&vboot_region[rmodule_offset], &vbootstub)) { + printk(BIOS_DEBUG, "Couldn't parse vboot stub rmodule.\n"); + goto out; + } + + if (rmodule_load(&vboot_region[load_offset], &vbootstub)) { + printk(BIOS_DEBUG, "Couldn't load vboot stub.\n"); + goto out; + } + + entry = rmodule_entry(&vbootstub); + + /* Call stub. */ + entry(context); + +out: + /* Tear down the region no longer needed. */ + cbmem_entry_remove(vboot_entry); +} + +/* Helper routines for the vboot stub. */ +static void log_msg(const char *fmt, va_list args) +{ + vtxprintf(console_tx_byte, fmt, args); + console_tx_flush(); +} + +static void fatal_error(void) +{ + printk(BIOS_ERR, "vboot encountered fatal error. Reseting.\n"); + hard_reset(); +} + +static void vboot_invoke_wrapper(struct vboot_handoff *vboot_handoff) +{ + VbCommonParams cparams; + VbSelectFirmwareParams fparams; + struct vboot_context context; + uint32_t *iflags; + + vboot_handoff->selected_firmware = VB_SELECT_FIRMWARE_READONLY; + + memset(&cparams, 0, sizeof(cparams)); + memset(&fparams, 0, sizeof(fparams)); + memset(&context, 0, sizeof(context)); + + iflags = &vboot_handoff->init_params.flags; + if (get_developer_mode_switch()) + *iflags |= VB_INIT_FLAG_DEV_SWITCH_ON; + if (get_recovery_mode_switch()) + *iflags |= VB_INIT_FLAG_REC_BUTTON_PRESSED; + if (get_write_protect_state()) + *iflags |= VB_INIT_FLAG_WP_ENABLED; + if (CONFIG_VIRTUAL_DEV_SWITCH) + *iflags |= VB_INIT_FLAG_VIRTUAL_DEV_SWITCH; + if (CONFIG_EC_SOFTWARE_SYNC) + *iflags |= VB_INIT_FLAG_EC_SOFTWARE_SYNC; + + context.handoff = vboot_handoff; + context.cparams = &cparams; + context.fparams = &fparams; + + cparams.gbb_size = find_fmap_entry("GBB", &cparams.gbb_data); + cparams.shared_data_blob = &vboot_handoff->shared_data[0]; + cparams.shared_data_size = VB_SHARED_DATA_MIN_SIZE; + cparams.caller_context = &context; + + fparams.verification_size_A = + find_fmap_entry("VBLOCK_A", &fparams.verification_block_A); + fparams.verification_size_B = + find_fmap_entry("VBLOCK_B", &fparams.verification_block_B); + + context.fw_a_size = + find_fmap_entry("FW_MAIN_A", (void **)&context.fw_a); + context.fw_b_size = + find_fmap_entry("FW_MAIN_B", (void **)&context.fw_b); + + /* Check all fmap entries. */ + if (context.fw_a == NULL || context.fw_b == NULL || + fparams.verification_block_A == NULL || + fparams.verification_block_B == NULL || + cparams.gbb_data == NULL) { + printk(BIOS_DEBUG, "Not all fmap entries found for vboot.\n"); + return; + } + + /* Initialize callbacks. */ + context.read_vbnv = &read_vbnv; + context.save_vbnv = &save_vbnv; + context.tis_init = &tis_init; + context.tis_open = &tis_open; + context.tis_close = &tis_close; + context.tis_sendrecv = &tis_sendrecv; + context.log_msg = &log_msg; + context.fatal_error = &fatal_error; + + vboot_run_stub(&context); +} + +static void vboot_load_ramstage(struct vboot_handoff *vboot_handoff, + struct romstage_handoff *handoff) +{ + struct cbfs_stage *stage; + struct rmodule ramstage; + void *entry_point; + size_t region_size; + char *ramstage_region; + int rmodule_offset; + int load_offset; + const struct cbmem_entry *ramstage_entry; + const struct firmware_component *fwc; + + if (CONFIG_VBOOT_RAMSTAGE_INDEX >= MAX_PARSED_FW_COMPONENTS) { + printk(BIOS_ERR, "Invalid ramstage index: %d\n", + CONFIG_VBOOT_RAMSTAGE_INDEX); + return; + } + + /* Check for invalid address. */ + fwc = &vboot_handoff->components[CONFIG_VBOOT_RAMSTAGE_INDEX]; + if (fwc->address == 0) { + printk(BIOS_DEBUG, "RW ramstage image address invalid.\n"); + return; + } + + printk(BIOS_DEBUG, "RW ramstage image at 0x%08x, 0x%08x bytes.\n", + fwc->address, fwc->size); + + stage = (void *)fwc->address; + + rmodule_offset = + rmodule_calc_region(DYN_CBMEM_ALIGN_SIZE, + stage->memlen, ®ion_size, &load_offset); + + ramstage_entry = cbmem_entry_add(CBMEM_ID_RAMSTAGE, region_size); + + if (ramstage_entry == NULL) { + vboot_handoff->selected_firmware = VB_SELECT_FIRMWARE_READONLY; + printk(BIOS_DEBUG, "Could not add ramstage region.\n"); + return; + } + + timestamp_add_now(TS_START_COPYRAM); + + ramstage_region = cbmem_entry_start(ramstage_entry); + + printk(BIOS_DEBUG, "Decompressing ramstage @ 0x%p (%d bytes)\n", + &ramstage_region[rmodule_offset], stage->memlen); + + if (cbfs_decompress(stage->compression, &stage[1], + &ramstage_region[rmodule_offset], stage->len)) + return; + + if (rmodule_parse(&ramstage_region[rmodule_offset], &ramstage)) + return; + + /* The ramstage is responsible for clearing its own bss. */ + if (rmodule_load_no_clear_bss(&ramstage_region[load_offset], &ramstage)) + return; + + entry_point = rmodule_entry(&ramstage); + + cache_loaded_ramstage(handoff, ramstage_entry, entry_point); + + timestamp_add_now(TS_END_COPYRAM); + + __asm__ volatile ( + "movl $0, %%ebp\n" + "jmp *%%edi\n" + :: "D"(entry_point) + ); +} + +void vboot_verify_firmware(struct romstage_handoff *handoff) +{ + struct vboot_handoff *vboot_handoff; + + /* Don't go down verified boot path on S3 resume. */ + if (handoff != NULL && handoff->s3_resume) + return; + + timestamp_add_now(TS_START_VBOOT); + + vboot_handoff = cbmem_add(CBMEM_ID_VBOOT_HANDOFF, + sizeof(*vboot_handoff)); + + if (vboot_handoff == NULL) { + printk(BIOS_DEBUG, "Could not add vboot_handoff structure.\n"); + return; + } + + memset(vboot_handoff, 0, sizeof(*vboot_handoff)); + + vboot_invoke_wrapper(vboot_handoff); + + timestamp_add_now(TS_END_VBOOT); + + /* Take RO firmware path since no RW area was selected. */ + if (vboot_handoff->selected_firmware != VB_SELECT_FIRMWARE_A && + vboot_handoff->selected_firmware != VB_SELECT_FIRMWARE_B) { + printk(BIOS_DEBUG, "No RW firmware selected: 0x%08x\n", + vboot_handoff->selected_firmware); + return; + } + + /* Load ramstage from the vboot_handoff structure. */ + vboot_load_ramstage(vboot_handoff, handoff); +} |