From fd79562915bbdea93fc7b37e657856acf808e64f Mon Sep 17 00:00:00 2001 From: Aaron Durbin Date: Fri, 1 Mar 2013 17:12:26 -0600 Subject: [PATCH] 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 Reviewed-on: http://review.coreboot.org/2854 Tested-by: build bot (Jenkins) Reviewed-by: Ronald G. Minnich --- src/arch/x86/Makefile.inc | 5 +- src/lib/cbfs.c | 15 +- src/southbridge/intel/lynxpoint/spi_loading.c | 15 +- src/vendorcode/google/chromeos/Kconfig | 40 +++ src/vendorcode/google/chromeos/Makefile.inc | 44 +++ src/vendorcode/google/chromeos/chromeos.c | 32 ++ src/vendorcode/google/chromeos/chromeos.h | 12 + src/vendorcode/google/chromeos/gnvs.c | 18 ++ src/vendorcode/google/chromeos/vbnv.c | 26 +- .../google/chromeos/vboot_context.h | 48 +++ .../google/chromeos/vboot_handoff.h | 53 ++++ src/vendorcode/google/chromeos/vboot_loader.c | 280 ++++++++++++++++++ .../google/chromeos/vboot_wrapper.c | 268 +++++++++++++++++ 13 files changed, 847 insertions(+), 9 deletions(-) create mode 100644 src/vendorcode/google/chromeos/vboot_context.h create mode 100644 src/vendorcode/google/chromeos/vboot_handoff.h create mode 100644 src/vendorcode/google/chromeos/vboot_loader.c create mode 100644 src/vendorcode/google/chromeos/vboot_wrapper.c diff --git a/src/arch/x86/Makefile.inc b/src/arch/x86/Makefile.inc index 7cda5b6d8b..377a86388e 100644 --- a/src/arch/x86/Makefile.inc +++ b/src/arch/x86/Makefile.inc @@ -76,7 +76,7 @@ $(obj)/coreboot.pre1: $(CBFSTOOL) mv $(obj)/coreboot.rom $@ endif -$(obj)/coreboot.rom: $(obj)/coreboot.pre $(objcbfs)/coreboot_ram.elf $(CBFSTOOL) $(call strip_quotes,$(COREBOOT_ROM_DEPENDENCIES)) $$(INTERMEDIATE) +$(obj)/coreboot.rom: $(obj)/coreboot.pre $(objcbfs)/coreboot_ram.elf $(CBFSTOOL) $(call strip_quotes,$(COREBOOT_ROM_DEPENDENCIES)) $$(INTERMEDIATE) $$(VBOOT_STUB_ELF) @printf " CBFS $(subst $(obj)/,,$(@))\n" cp $(obj)/coreboot.pre $@.tmp if [ -f $(objcbfs)/coreboot_ap.elf ]; \ @@ -109,6 +109,9 @@ ifeq ($(CONFIG_INCLUDE_CONFIG_FILE),y) echo "# This image was built using git revision" `git rev-parse HEAD` > $(obj)/config.tmp ; \ sed -e '/^#/d' -e '/^ *$$/d' $(DOTCONFIG) >> $(obj)/config.tmp ; \ $(CBFSTOOL) $@.tmp add -f $(obj)/config.tmp -n config -t raw; rm -f $(obj)/config.tmp ; fi +endif +ifeq ($(CONFIG_VBOOT_VERIFY_FIRMWARE),y) + $(CBFSTOOL) $@.tmp add-stage -f $(VBOOT_STUB_ELF) -n $(CONFIG_CBFS_PREFIX)/vboot -c $(CBFS_COMPRESS_FLAG) endif mv $@.tmp $@ @printf " CBFSPRINT $(subst $(obj)/,,$(@))\n\n" diff --git a/src/lib/cbfs.c b/src/lib/cbfs.c index 45ae7c7683..39e6c11000 100644 --- a/src/lib/cbfs.c +++ b/src/lib/cbfs.c @@ -63,6 +63,12 @@ #include "cbfs_core.c" +#if CONFIG_VBOOT_VERIFY_FIRMWARE +#include +#else +static inline void *vboot_get_payload(int *len) { return NULL; } +#endif + #ifndef __SMM__ static inline int tohex4(unsigned int c) { @@ -302,8 +308,15 @@ int cbfs_execute_stage(struct cbfs_media *media, const char *name) #if !CONFIG_ALT_CBFS_LOAD_PAYLOAD void *cbfs_load_payload(struct cbfs_media *media, const char *name) { - return (struct cbfs_payload *)cbfs_get_file_content( + struct cbfs_payload *payload; + + payload = vboot_get_payload(NULL); + if (payload != NULL) + return payload; + + payload = (struct cbfs_payload *)cbfs_get_file_content( media, name, CBFS_TYPE_PAYLOAD); + return payload; } #endif diff --git a/src/southbridge/intel/lynxpoint/spi_loading.c b/src/southbridge/intel/lynxpoint/spi_loading.c index eb3082bb5a..1ae7a263eb 100644 --- a/src/southbridge/intel/lynxpoint/spi_loading.c +++ b/src/southbridge/intel/lynxpoint/spi_loading.c @@ -21,9 +21,15 @@ #include #include #include +#include #include #include #include +#if CONFIG_VBOOT_VERIFY_FIRMWARE +#include +#else +static inline void *vboot_get_payload(int *len) { return NULL; } +#endif #define CACHELINE_SIZE 64 #define INTRA_CACHELINE_MASK (CACHELINE_SIZE - 1) @@ -69,7 +75,14 @@ void *cbfs_load_payload(struct cbfs_media *media, const char *name) { int file_len; void *file_start; - struct cbfs_file *file = cbfs_get_file(media, name); + struct cbfs_file *file; + + file_start = vboot_get_payload(&file_len); + + if (file_start != NULL) + return spi_mirror(file_start, file_len); + + file = cbfs_get_file(media, name); if (file == NULL) return NULL; diff --git a/src/vendorcode/google/chromeos/Kconfig b/src/vendorcode/google/chromeos/Kconfig index 31094de956..06ed7d3255 100644 --- a/src/vendorcode/google/chromeos/Kconfig +++ b/src/vendorcode/google/chromeos/Kconfig @@ -67,6 +67,46 @@ config FLASHMAP_OFFSET endmenu +config VBOOT_VERIFY_FIRMWARE + bool "Verify firmware with vboot." + default n + depends on CHROMEOS + help + Enabling VBOOT_VERIFY_FIRMWARE will use vboot to verify the ramstage + and boot loader. + +config EC_SOFTWARE_SYNC + bool "Enable EC software sync" + default n + depends on VBOOT_VERIFY_FIRMWARE + help + EC software sync is a mechanism where the AP helps the EC verify its + firmware similar to how vboot verifies the main system firmware. This + option selects whether depthcharge should support EC software sync. + +config VIRTUAL_DEV_SWITCH + bool "Virtual developer switch support" + default n + depends on VBOOT_VERIFY_FIRMWARE + help + Whether this platform has a virtual developer switch. + +config VBOOT_BOOT_LOADER_INDEX + hex "Bootloader component index" + default 0 + depends on VBOOT_VERIFY_FIRMWARE + help + This is the index of the bootloader component in the verified + firmware block. + +config VBOOT_RAMSTAGE_INDEX + hex "Ramstage component index" + default 1 + depends on VBOOT_VERIFY_FIRMWARE + help + This is the index of the ramstage component in the verified + firmware block. + config NO_TPM_RESUME bool default n diff --git a/src/vendorcode/google/chromeos/Makefile.inc b/src/vendorcode/google/chromeos/Makefile.inc index 8ae14fdb0b..9bc4d64163 100644 --- a/src/vendorcode/google/chromeos/Makefile.inc +++ b/src/vendorcode/google/chromeos/Makefile.inc @@ -30,3 +30,47 @@ ifneq ($(wildcard src/mainboard/$(MAINBOARDDIR)/chromeos.c),) ramstage-srcs += src/mainboard/$(MAINBOARDDIR)/chromeos.c romstage-srcs += src/mainboard/$(MAINBOARDDIR)/chromeos.c endif + +ifeq ($(CONFIG_VBOOT_VERIFY_FIRMWARE),y) +romstage-y += vboot_loader.c +rmodules-y += vboot_wrapper.c + +VB_LIB = $(obj)/external/vboot_reference/vboot_fw.a +VB_FIRMWARE_ARCH := $(ARCHDIR-y) +VB_SOURCE := vboot_reference + +# Add the vboot include paths. +VB_INCLUDES += -I$(VB_SOURCE)/firmware/include +VB_INCLUDES += -I$(VB_SOURCE)/firmware/arch/$(VB_FIRMWARE_ARCH)/include +INCLUDES += $(VB_INCLUDES) + +VBOOT_STUB_ELF = $(obj)/vendorcode/google/chromeos/vbootstub.elf +VBOOT_STUB_DOTO = $(VBOOT_STUB_ELF:.elf=.o) + +# Dependency for the vboot rmodules. Ordering matters. +VBOOT_STUB_DEPS += $(obj)/vendorcode/google/chromeos/vboot_wrapper.rmodules.o +VBOOT_STUB_DEPS += $(obj)/lib/memcmp.rmodules.o +VBOOT_STUB_DEPS += $(obj)/arch/x86/lib/memset.rmodules.o +VBOOT_STUB_DEPS += $(obj)/arch/x86/lib/memcpy.rmodules.o +VBOOT_STUB_DEPS += $(VB_LIB) +# Remove the '-include' option since that will break vboot's build. +VBOOT_CFLAGS += $(filter-out -include $(src)/include/kconfig.h, $(CFLAGS)) +VBOOT_CFLAGS += -DVBOOT_DEBUG + +$(VBOOT_STUB_DOTO): $(VBOOT_STUB_DEPS) + $(CC) $(LDFLAGS) -nostdlib -r -o $@ $^ + +# Link the vbootstub module with a 64KiB-byte heap. +$(eval $(call rmodule_link,$(VBOOT_STUB_ELF), $(VBOOT_STUB_DOTO), 0x10000)) + +# Build vboot library without the default includes from coreboot proper. +$(VB_LIB): + @printf " MAKE $(subst $(obj)/,,$(@))\n" + $(Q)FIRMWARE_ARCH=$(VB_FIRMWARE_ARCH) \ + CFLAGS="$(VBOOT_CFLAGS)" \ + make -C $(VB_SOURCE) \ + BUILD=../$(dir $(VB_LIB)) \ + V=$(V) \ + fwlib + +endif diff --git a/src/vendorcode/google/chromeos/chromeos.c b/src/vendorcode/google/chromeos/chromeos.c index c1c3b3834c..559f1f0fdd 100644 --- a/src/vendorcode/google/chromeos/chromeos.c +++ b/src/vendorcode/google/chromeos/chromeos.c @@ -18,7 +18,11 @@ */ #include "chromeos.h" +#if CONFIG_VBOOT_VERIFY_FIRMWARE +#include "vboot_handoff.h" +#endif #include +#include #include int developer_mode_enabled(void) @@ -35,3 +39,31 @@ int recovery_mode_enabled(void) return get_recovery_mode_switch() || get_recovery_mode_from_vbnv(); } +#if CONFIG_VBOOT_VERIFY_FIRMWARE +void *vboot_get_payload(int *len) +{ + struct vboot_handoff *vboot_handoff; + struct firmware_component *fwc; + + vboot_handoff = cbmem_find(CBMEM_ID_VBOOT_HANDOFF); + + if (vboot_handoff == NULL) + return NULL; + + if (CONFIG_VBOOT_BOOT_LOADER_INDEX >= MAX_PARSED_FW_COMPONENTS) { + printk(BIOS_ERR, "Invalid boot loader index: %d\n", + CONFIG_VBOOT_BOOT_LOADER_INDEX); + return NULL; + } + + fwc = &vboot_handoff->components[CONFIG_VBOOT_BOOT_LOADER_INDEX]; + + if (len != NULL) + *len = fwc->size; + + printk(BIOS_DEBUG, "Booting 0x%x byte payload at 0x%08x.\n", + fwc->size, fwc->address); + + return (void *)fwc->address; +} +#endif diff --git a/src/vendorcode/google/chromeos/chromeos.h b/src/vendorcode/google/chromeos/chromeos.h index 37630a69bc..8410707a9b 100644 --- a/src/vendorcode/google/chromeos/chromeos.h +++ b/src/vendorcode/google/chromeos/chromeos.h @@ -20,9 +20,12 @@ #ifndef __CHROMEOS_H__ #define __CHROMEOS_H__ +#include + /* functions implemented per mainboard: */ int get_developer_mode_switch(void); int get_recovery_mode_switch(void); +int get_write_protect_state(void); #ifdef __PRE_RAM__ void save_chromeos_gpios(void); #endif @@ -32,6 +35,9 @@ int get_recovery_mode_from_vbnv(void); int vboot_wants_oprom(void); extern int oprom_is_loaded; +void read_vbnv(uint8_t *vbnv_copy); +void save_vbnv(const uint8_t *vbnv_copy); + /* functions implemented in chromeos.c: */ int developer_mode_enabled(void); int recovery_mode_enabled(void); @@ -39,4 +45,10 @@ int recovery_mode_enabled(void); /* functions implemented in vboot.c */ void init_chromeos(int bootmode); +#if CONFIG_VBOOT_VERIFY_FIRMWARE +struct romstage_handoff; +void vboot_verify_firmware(struct romstage_handoff *handoff); +void *vboot_get_payload(int *len); +#endif + #endif diff --git a/src/vendorcode/google/chromeos/gnvs.c b/src/vendorcode/google/chromeos/gnvs.c index 2e9975c9cb..0d4095061d 100644 --- a/src/vendorcode/google/chromeos/gnvs.c +++ b/src/vendorcode/google/chromeos/gnvs.c @@ -19,12 +19,17 @@ #include #include +#include #include +#include #include #include #include "chromeos.h" #include "gnvs.h" +#if CONFIG_VBOOT_VERIFY_FIRMWARE +#include "vboot_handoff.h" +#endif chromeos_acpi_t *vboot_data = NULL; static u32 me_hash_saved[8]; @@ -36,6 +41,19 @@ void chromeos_init_vboot(chromeos_acpi_t *chromeos) /* Copy saved ME hash into NVS */ memcpy(vboot_data->mehh, me_hash_saved, sizeof(vboot_data->mehh)); +#if CONFIG_VBOOT_VERIFY_FIRMWARE + /* Save the vdat from the vboot handoff structure. Downstream software + * consumes the data located in the ACPI table. Ensure it reflects + * the shared data from VbInit() and VbSelectFirmware(). */ + struct vboot_handoff *vboot_handoff; + + vboot_handoff = cbmem_find(CBMEM_ID_VBOOT_HANDOFF); + + if (vboot_handoff != NULL) + memcpy(&chromeos->vdat[0], &vboot_handoff->shared_data[0], + ARRAY_SIZE(chromeos->vdat)); +#endif + #if CONFIG_ELOG if (developer_mode_enabled() || (vboot_wants_oprom() && !recovery_mode_enabled())) diff --git a/src/vendorcode/google/chromeos/vbnv.c b/src/vendorcode/google/chromeos/vbnv.c index 3f333f7211..2a2faf9e96 100644 --- a/src/vendorcode/google/chromeos/vbnv.c +++ b/src/vendorcode/google/chromeos/vbnv.c @@ -77,25 +77,39 @@ static uint8_t crc8(const uint8_t * data, int len) return (uint8_t) (crc >> 8); } -static void vbnv_setup(void) +void read_vbnv(uint8_t *vbnv_copy) { int i; for (i = 0; i < CONFIG_VBNV_SIZE; i++) - vbnv[i] = cmos_read(CONFIG_VBNV_OFFSET + 14 + i); + vbnv_copy[i] = cmos_read(CONFIG_VBNV_OFFSET + 14 + i); /* Check data for consistency */ - if ((HEADER_SIGNATURE != (vbnv[HEADER_OFFSET] & HEADER_MASK)) - || (crc8(vbnv, CRC_OFFSET) != vbnv[CRC_OFFSET])) { + if ((HEADER_SIGNATURE != (vbnv_copy[HEADER_OFFSET] & HEADER_MASK)) + || (crc8(vbnv_copy, CRC_OFFSET) != vbnv_copy[CRC_OFFSET])) { /* Data is inconsistent (bad CRC or header), * so reset to defaults */ - memset(vbnv, 0, VBNV_BLOCK_SIZE); - vbnv[HEADER_OFFSET] = + memset(vbnv_copy, 0, VBNV_BLOCK_SIZE); + vbnv_copy[HEADER_OFFSET] = (HEADER_SIGNATURE | HEADER_FIRMWARE_SETTINGS_RESET | HEADER_KERNEL_SETTINGS_RESET); } +} + +void save_vbnv(const uint8_t *vbnv_copy) +{ + int i; + + for (i = 0; i < CONFIG_VBNV_SIZE; i++) + cmos_write(vbnv_copy[i], CONFIG_VBNV_OFFSET + 14 + i); +} + + +static void vbnv_setup(void) +{ + read_vbnv(vbnv); vbnv_initialized = 1; } diff --git a/src/vendorcode/google/chromeos/vboot_context.h b/src/vendorcode/google/chromeos/vboot_context.h new file mode 100644 index 0000000000..72a05350cd --- /dev/null +++ b/src/vendorcode/google/chromeos/vboot_context.h @@ -0,0 +1,48 @@ +/* + * 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 + */ +#ifndef VBOOT_CONTEXT_H +#define VBOOT_CONTEXT_H + +#include +#include + +/* The vboot context structure provides all the necessary data for invoking + * vboot. The vboot loader sets everything up for vboot module to use. */ + +struct vboot_context { + struct vboot_handoff *handoff; + VbCommonParams *cparams; + VbSelectFirmwareParams *fparams; + uint8_t *fw_a; + uint32_t fw_a_size; + uint8_t *fw_b; + uint32_t fw_b_size; + /* Callback implementations living in romstage. */ + void (*read_vbnv)(uint8_t *vbnv_copy); + void (*save_vbnv)(const uint8_t *vbnv_copy); + int (*tis_init)(void); + int (*tis_open)(void); + int (*tis_close)(void); + int (*tis_sendrecv)(const u8 *sendbuf, size_t send_size, u8 *recvbuf, + size_t *recv_len); + void (*log_msg)(const char *fmt, va_list args); + void (*fatal_error)(void); +}; + +#endif /* VBOOT_CONTEXT_H */ diff --git a/src/vendorcode/google/chromeos/vboot_handoff.h b/src/vendorcode/google/chromeos/vboot_handoff.h new file mode 100644 index 0000000000..09c7897f54 --- /dev/null +++ b/src/vendorcode/google/chromeos/vboot_handoff.h @@ -0,0 +1,53 @@ +/* + * 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 + */ +#ifndef VBOOT_HANDOFF_H +#define VBOOT_HANDOFF_H + +#include + +/* + * The vboot handoff structure keeps track of a maximum number of firmware + * components in the verfieid RW area of flash. This is not a restriction on + * the number of components packed in a firmware block. It's only the maximum + * number of parsed firmware components (address and size) included in the + * handoff structure. + */ + +#define MAX_PARSED_FW_COMPONENTS 5 + +struct firmware_component { + uint32_t address; + uint32_t size; +} __attribute__((packed)); + +/* + * The vboot_handoff structure contains the data to be consumed by downstream + * firmware after firmware selection has been completed. Namely it provides + * vboot shared data as well as the flags from VbInit. As noted above a finite + * number of components are parsed from the verfieid firmare region. + */ +struct vboot_handoff { + VbInitParams init_params; + uint32_t selected_firmware; + struct firmware_component components[MAX_PARSED_FW_COMPONENTS]; + char shared_data[VB_SHARED_DATA_MIN_SIZE]; +} __attribute__((packed)); + + +#endif /* VBOOT_HANDOFF_H */ 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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); +} diff --git a/src/vendorcode/google/chromeos/vboot_wrapper.c b/src/vendorcode/google/chromeos/vboot_wrapper.c new file mode 100644 index 0000000000..66b7cfb276 --- /dev/null +++ b/src/vendorcode/google/chromeos/vboot_wrapper.c @@ -0,0 +1,268 @@ +/* + * 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 +#include +#include +#include +#include +#include "vboot_context.h" +#include "vboot_handoff.h" + +static void vboot_wrapper(struct vboot_context *context); + +DEFINE_RMODULE_HEADER(vboot_wrapper_header, vboot_wrapper, RMODULE_TYPE_VBOOT); + +/* Keep a global context pointer around for the callbacks to use. */ +static struct vboot_context *gcontext; + +/* The FW areas consist of multiple components. At the beginning of + * each area is the number of total compoments as well as the size and + * offset for each component. One needs to caculate the total size of the + * signed firmware region based off of the embedded metadata. */ +#define MAX_NUM_COMPONENTS 20 + +struct component_entry { + uint32_t offset; + uint32_t size; +} __attribute__((packed)); + +struct components { + uint32_t num_components; + struct component_entry entries[0]; +} __attribute__((packed)); + + +static void parse_component(const struct components *components, int num, + struct firmware_component *fw) +{ + const char *base; + + if (num >= components->num_components) + return; + + /* Offsets are relative to the stat of the book keeping structure. */ + base = (void *)components; + + fw->address = (uint32_t)&base[components->entries[num].offset]; + fw->size = (uint32_t)components->entries[num].size; +} + +static void vboot_wrapper(struct vboot_context *context) +{ + int i; + VbError_t res; + const struct components *components; + + gcontext = context; + + VbExDebug("Calling VbInit()\n"); + res = VbInit(context->cparams, &context->handoff->init_params); + VbExDebug("VbInit() returned 0x%08x\n", res); + + if (res != VBERROR_SUCCESS) + return; + + VbExDebug("Calling VbSelectFirmware()\n"); + res = VbSelectFirmware(context->cparams, context->fparams); + VbExDebug("VbSelectFirmware() returned 0x%08x\n", res); + + if (res != VBERROR_SUCCESS) + return; + + /* Fix up the handoff structure. */ + context->handoff->selected_firmware = + context->fparams->selected_firmware; + + /* Parse out the components for downstream consumption. */ + if (context->handoff->selected_firmware == VB_SELECT_FIRMWARE_A) + components = (void *)context->fw_a; + else if (context->handoff->selected_firmware == VB_SELECT_FIRMWARE_B) + components = (void *)context->fw_b; + else + return; + + for (i = 0; i < MAX_PARSED_FW_COMPONENTS; i++) { + parse_component(components, i, + &context->handoff->components[i]); + } +} + +void VbExError(const char *format, ...) +{ + va_list args; + + va_start(args, format); + gcontext->log_msg(format, args); + va_end(args); + + gcontext->fatal_error(); +} + +void VbExDebug(const char *format, ...) +{ + va_list args; + + va_start(args, format); + gcontext->log_msg(format, args); + va_end(args); +} + +uint64_t VbExGetTimer(void) +{ + return rdtscll(); +} + +VbError_t VbExNvStorageRead(uint8_t *buf) +{ + gcontext->read_vbnv(buf); + return VBERROR_SUCCESS; +} + +VbError_t VbExNvStorageWrite(const uint8_t *buf) +{ + gcontext->save_vbnv(buf); + return VBERROR_SUCCESS; +} + +extern char _heap[]; +extern char _eheap[]; +static char *heap_current; +static int heap_size; + +void *VbExMalloc(size_t size) +{ + void *ptr; + + if (heap_current == NULL) { + heap_current = &_heap[0]; + heap_size = &_eheap[0] - &_heap[0]; + VbExDebug("vboot heap: %p 0x%08x bytes\n", + heap_current, heap_size); + } + + if (heap_size < size) { + VbExError("vboot heap request cannot be fulfilled. " + "0x%08x available, 0x%08x requested\n", + heap_size, size); + } + + ptr = heap_current; + heap_size -= size; + heap_current += size; + + return ptr; +} + +void VbExFree(void *ptr) +{ + /* Leak all memory. */ +} + +/* vboot doesn't expose these through the vboot_api.h, but they are needed. + * coreboot requires declarations so provide them to avoid compiler errors. */ +int Memcmp(const void *src1, const void *src2, size_t n); +void *Memcpy(void *dest, const void *src, uint64_t n); +void *Memset(void *dest, const uint8_t c, uint64_t n); + +int Memcmp(const void *src1, const void *src2, size_t n) +{ + return memcmp(src1, src2, n); +} + +void *Memcpy(void *dest, const void *src, uint64_t n) +{ + return memcpy(dest, src, n); +} + +void *Memset(void *dest, const uint8_t c, uint64_t n) +{ + return memset(dest, c, n); +} + +VbError_t VbExHashFirmwareBody(VbCommonParams *cparams, uint32_t firmware_index) +{ + uint8_t *data; + uint32_t size; + uint32_t data_size; + struct components *components; + uint32_t i; + + switch (firmware_index) { + case VB_SELECT_FIRMWARE_A: + data = gcontext->fw_a; + size = gcontext->fw_a_size; + break; + case VB_SELECT_FIRMWARE_B: + data = gcontext->fw_b; + size = gcontext->fw_b_size; + break; + default: + return VBERROR_UNKNOWN; + } + + components = (void *)data; + data_size = sizeof(struct components); + + if (components->num_components > MAX_NUM_COMPONENTS) + return VBERROR_UNKNOWN; + + data_size += + components->num_components * sizeof(struct component_entry); + + for (i = 0; i < components->num_components; i++) + data_size += ALIGN(components->entries[i].size, 4); + + if (size < data_size) + return VBERROR_UNKNOWN; + + VbUpdateFirmwareBodyHash(cparams, data, data_size); + + return VBERROR_SUCCESS; +} + +VbError_t VbExTpmInit(void) +{ + if (gcontext->tis_init()) + return VBERROR_UNKNOWN; + return VbExTpmOpen(); +} + +VbError_t VbExTpmClose(void) +{ + if (gcontext->tis_close()) + return VBERROR_UNKNOWN; + return VBERROR_SUCCESS; +} + +VbError_t VbExTpmOpen(void) +{ + if (gcontext->tis_open()) + return VBERROR_UNKNOWN; + return VBERROR_SUCCESS; +} + +VbError_t VbExTpmSendReceive(const uint8_t *request, uint32_t request_length, + uint8_t *response, uint32_t *response_length) +{ + if (gcontext->tis_sendrecv(request, request_length, + response, response_length)) + return VBERROR_UNKNOWN; + return VBERROR_SUCCESS; +} +