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; +} +