diff options
Diffstat (limited to 'src/vendorcode/google')
-rw-r--r-- | src/vendorcode/google/chromeos/Kconfig | 39 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/Makefile.inc | 32 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/chromeos.c | 284 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/chromeos.h | 61 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot_context.h | 11 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot_handoff.c | 77 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot_handoff.h | 11 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/vboot_loader.c | 54 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/verstage.c | 250 | ||||
-rw-r--r-- | src/vendorcode/google/chromeos/verstub.c | 93 |
10 files changed, 619 insertions, 293 deletions
diff --git a/src/vendorcode/google/chromeos/Kconfig b/src/vendorcode/google/chromeos/Kconfig index 69ecaf2b02..4e71c3bfe5 100644 --- a/src/vendorcode/google/chromeos/Kconfig +++ b/src/vendorcode/google/chromeos/Kconfig @@ -98,17 +98,17 @@ config VBOOT_VERIFY_FIRMWARE and boot loader. config VBOOT2_VERIFY_FIRMWARE - bool "Firmware Verification with vboot2" - default n - depends on CHROMEOS - help + bool "Firmware Verification with vboot2" + default n + depends on CHROMEOS + help Enabling VBOOT2_VERIFY_FIRMWARE will use vboot2 to verify the romstage and boot loader. config EC_SOFTWARE_SYNC bool "Enable EC software sync" default n - depends on VBOOT_VERIFY_FIRMWARE + depends on VBOOT_VERIFY_FIRMWARE || VBOOT2_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 @@ -117,22 +117,45 @@ config EC_SOFTWARE_SYNC config VIRTUAL_DEV_SWITCH bool "Virtual developer switch support" default n - depends on VBOOT_VERIFY_FIRMWARE + depends on VBOOT_VERIFY_FIRMWARE || VBOOT2_VERIFY_FIRMWARE help Whether this platform has a virtual developer switch. +config RETURN_FROM_VERSTAGE + bool "return from verstage" + default n + depends on VBOOT2_VERIFY_FIRMWARE + help + If this is set, the verstage returns back to the bootblock instead of + exits to the romstage so that the verstage space can be reused by the + romstage. Useful if a ram space is too small to fit both the verstage + and the romstage. + +# These VBOOT_X_INDEX are the position of X in FW_MAIN_A/B region. The index +# table is created by cros_bundle_firmware at build time based on the positions +# of the blobs listed in fmap.dts and stored at the top of FW_MAIN_A/B region. +# Unfortunately, there is no programmatical link between the blob list and the +# index number here. config VBOOT_BOOT_LOADER_INDEX hex "Bootloader component index" default 0 - depends on VBOOT_VERIFY_FIRMWARE + depends on VBOOT_VERIFY_FIRMWARE || VBOOT2_VERIFY_FIRMWARE help This is the index of the bootloader component in the verified firmware block. +config VBOOT_ROMSTAGE_INDEX + hex + default 2 + depends on VBOOT2_VERIFY_FIRMWARE + help + This is the index of the romstage component in the verified + firmware block. + config VBOOT_RAMSTAGE_INDEX hex "Ramstage component index" default 1 - depends on VBOOT_VERIFY_FIRMWARE + depends on VBOOT_VERIFY_FIRMWARE || VBOOT2_VERIFY_FIRMWARE help This is the index of the ramstage component in the verified firmware block. diff --git a/src/vendorcode/google/chromeos/Makefile.inc b/src/vendorcode/google/chromeos/Makefile.inc index 701feb7b7f..e9eef4ac19 100644 --- a/src/vendorcode/google/chromeos/Makefile.inc +++ b/src/vendorcode/google/chromeos/Makefile.inc @@ -71,7 +71,7 @@ VBOOT_STUB_DEPS += $(obj)/arch/x86/lib/memcpy.rmodules_$(ARCH-romstage-y).o VBOOT_STUB_DEPS += $(VB_LIB) # Remove the '-include' option since that will break vboot's build and ensure # vboot_reference can get to coreboot's include files. -VBOOT_CFLAGS += $(patsubst -I%,-I../%,$(filter-out -include $(src)/include/kconfig.h, $(CFLAGS_romstage))) +VBOOT_CFLAGS += $(patsubst -I%,-I$(top)/%,$(filter-out -include $(src)/include/kconfig.h, $(CFLAGS_romstage))) VBOOT_CFLAGS += -DVBOOT_DEBUG VBOOT_CFLAGS += $(rmodules_$(ARCH-ROMSTAGE-y)-c-ccopts) @@ -89,7 +89,7 @@ $(VB_LIB): CFLAGS="$(VBOOT_CFLAGS)" \ make -C $(VB_SOURCE) \ $(VBOOT_MAKEFLAGS) \ - BUILD=../$(dir $(VB_LIB)) \ + BUILD=$(top)/$(dir $(VB_LIB)) \ V=$(V) \ fwlib @@ -97,10 +97,17 @@ endif ifeq ($(CONFIG_VBOOT2_VERIFY_FIRMWARE),y) VB_SOURCE := vboot_reference -VERSTAGE_LIB = $(obj)/vendorcode/google/chromeos/verstage.a - INCLUDES += -I$(VB_SOURCE)/firmware/2lib/include INCLUDES += -I$(VB_SOURCE)/firmware/include + +verstage-c-ccopts += -D__PRE_RAM__ -D__VER_STAGE__ +verstage-S-ccopts += -D__PRE_RAM__ -D__VER_STAGE__ + +ifeq ($(CONFIG_RETURN_FROM_VERSTAGE),y) +bootblock-y += verstub.c +else +verstage-y += verstub.c +endif verstage-y += verstage.c fmap.c chromeos.c verstage-y += antirollback.c vbnv_ec.c romstage-y += vboot_handoff.c @@ -121,13 +128,10 @@ $(VB2_LIB): $(obj)/config.h BUILD=$(top)/$(dir $(VB2_LIB)) \ V=$(V) \ fwlib2 - mv $@ $@.tmp - @printf " OBJCOPY $(subst $(obj)/,,$(@))\n" - $(OBJCOPY_verstage) --prefix-symbols=verstage_ $@.tmp $@ - -$(VERSTAGE_LIB): $$(verstage-objs) - @printf " AR $(subst $(obj)/,,$(@))\n" - $(AR_verstage) rc $@.tmp $(verstage-objs) - @printf " OBJCOPY $(subst $(obj)/,,$(@))\n" - $(OBJCOPY_verstage) --prefix-symbols=verstage_ $@.tmp $@ -endif + +VERSTAGE_ELF = $(objcbfs)/verstage.elf +cbfs-files-y += $(call strip_quotes,$(CONFIG_CBFS_PREFIX))/verstage +fallback/verstage-file = $(VERSTAGE_ELF) +fallback/verstage-type = stage +fallback/verstage-compression = none +endif # CONFIG_VBOOT2_VERIFY_FIRMWARE diff --git a/src/vendorcode/google/chromeos/chromeos.c b/src/vendorcode/google/chromeos/chromeos.c new file mode 100644 index 0000000000..d2f62d7da7 --- /dev/null +++ b/src/vendorcode/google/chromeos/chromeos.c @@ -0,0 +1,284 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2011 The ChromiumOS Authors. All rights reserved. + * + * 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 <stddef.h> +#include <string.h> +#include "chromeos.h" +#if CONFIG_VBOOT_VERIFY_FIRMWARE || CONFIG_VBOOT2_VERIFY_FIRMWARE +#include "fmap.h" +#include "vboot_handoff.h" +#include <reset.h> +#endif +#include <boot/coreboot_tables.h> +#include <cbfs.h> +#include <cbmem.h> +#include <console/console.h> + +#if CONFIG_VBOOT_VERIFY_FIRMWARE +static int vboot_enable_developer(void) +{ + struct vboot_handoff *vbho; + + vbho = cbmem_find(CBMEM_ID_VBOOT_HANDOFF); + + if (vbho == NULL) { + printk(BIOS_ERR, "%s: Couldn't find vboot_handoff structure!\n", + __func__); + return 0; + } + + return !!(vbho->init_params.out_flags & VB_INIT_OUT_ENABLE_DEVELOPER); +} + +static int vboot_enable_recovery(void) +{ + struct vboot_handoff *vbho; + + vbho = cbmem_find(CBMEM_ID_VBOOT_HANDOFF); + + if (vbho == NULL) + return 0; + + return !!(vbho->init_params.out_flags & VB_INIT_OUT_ENABLE_RECOVERY); +} +#else +static inline int vboot_enable_developer(void) { return 0; } +static inline int vboot_enable_recovery(void) { return 0; } +#endif + +int developer_mode_enabled(void) +{ + return get_developer_mode_switch() || vboot_enable_developer(); +} + +int recovery_mode_enabled(void) +{ + /* + * This is called in multiple places and has to detect + * recovery mode triggered from the EC and via shared + * recovery reason set with crossystem. + * + * If shared recovery reason is set: + * - before VbInit then get_recovery_mode_from_vbnv() is true + * - after VbInit then vboot_enable_recovery() is true + * + * Otherwise the mainboard handler for get_recovery_mode_switch() + * will detect recovery mode initiated by the EC. + */ + return get_recovery_mode_switch() || get_recovery_mode_from_vbnv() || + vboot_enable_recovery(); +} + +int __attribute__((weak)) clear_recovery_mode_switch(void) +{ + // Can be implemented by a mainboard + return 0; +} + +int vboot_skip_display_init(void) +{ +#if CONFIG_VBOOT_VERIFY_FIRMWARE + struct vboot_handoff *vbho; + + vbho = cbmem_find(CBMEM_ID_VBOOT_HANDOFF); + + if (vbho == NULL) + return 0; + + return !(vbho->init_params.out_flags & VB_INIT_OUT_ENABLE_DISPLAY); +#else + return 0; +#endif +} + +#ifdef __PRE_RAM__ +void __attribute__((weak)) save_chromeos_gpios(void) +{ + // Can be implemented by a mainboard +} + +int __attribute((weak)) vboot_get_sw_write_protect(void) +{ + // Can be implemented by a platform / mainboard + return 0; +} +#endif + +#if CONFIG_VBOOT_VERIFY_FIRMWARE || CONFIG_VBOOT2_VERIFY_FIRMWARE +void vboot_locate_region(const char *name, struct vboot_region *region) +{ + region->size = find_fmap_entry(name, (void **)®ion->offset_addr); +} + +void *vboot_get_region(uintptr_t offset_addr, size_t size, void *dest) +{ + if (IS_ENABLED(CONFIG_SPI_FLASH_MEMORY_MAPPED)) { + if (dest != NULL) + return memcpy(dest, (void *)offset_addr, size); + else + return (void *)offset_addr; + } else { + struct cbfs_media default_media, *media = &default_media; + void *cache; + + init_default_cbfs_media(media); + media->open(media); + if (dest != NULL) { + cache = dest; + if (media->read(media, dest, offset_addr, size) != size) + cache = NULL; + } else { + cache = media->map(media, offset_addr, size); + if (cache == CBFS_MEDIA_INVALID_MAP_ADDRESS) + cache = NULL; + } + media->close(media); + return cache; + } +} + +int vboot_get_handoff_info(void **addr, uint32_t *size) +{ + struct vboot_handoff *vboot_handoff; + + vboot_handoff = cbmem_find(CBMEM_ID_VBOOT_HANDOFF); + + if (vboot_handoff == NULL) + return -1; + + *addr = vboot_handoff; + *size = sizeof(*vboot_handoff); + return 0; +} + +/* This will leak a mapping of a fw region */ +struct vboot_components *vboot_locate_components(struct vboot_region *region) +{ + size_t req_size; + struct vboot_components *vbc; + + req_size = sizeof(*region); + req_size += sizeof(struct vboot_component_entry) * + MAX_PARSED_FW_COMPONENTS; + + vbc = vboot_get_region(region->offset_addr, req_size, NULL); + if (vbc && vbc->num_components > MAX_PARSED_FW_COMPONENTS) + vbc = NULL; + + return vbc; +} + +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 payload size is zero fall back to cbfs path. */ + if (fwc->size == 0) + return NULL; + + if (len != NULL) + *len = fwc->size; + + printk(BIOS_DEBUG, "Booting 0x%x byte verified payload at 0x%08x.\n", + fwc->size, fwc->address); + + /* This will leak a mapping. */ + return vboot_get_region(fwc->address, fwc->size, NULL); +} +#endif + +#if CONFIG_VBOOT2_VERIFY_FIRMWARE +void *vboot_load_stage(int stage_index, + struct vboot_region *fw_main, + struct vboot_components *fw_info) +{ + struct cbfs_stage *stage; + uint32_t fc_addr; + uint32_t fc_size; + + 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; + } + + /* Loading to cbfs cache. This stage data must be retained until it's + * decompressed. */ + stage = vboot_get_region(fc_addr, fc_size, NULL); + + if (stage == NULL) { + printk(BIOS_INFO, "failed to load a stage\n"); + return NULL; + } + + /* Stages rely the below clearing so that the bss is initialized. */ + memset((void *) (uintptr_t)stage->load, 0, stage->memlen); + + if (cbfs_decompress(stage->compression, + (unsigned char *)stage + sizeof(*stage), + (void *) (uintptr_t) stage->load, + stage->len)) { + printk(BIOS_INFO, "failed to decompress a stage\n"); + return NULL; + } + + return (void *)(uintptr_t)stage->entry; +} + +struct vb2_working_data * const vboot_get_working_data(void) +{ + return (struct vb2_working_data *)CONFIG_VBOOT_WORK_BUFFER_ADDRESS; +} + +int vboot_is_slot_selected(struct vb2_working_data *wd) +{ + return wd->selected_region.size > 0; +} + +int vboot_is_readonly_path(struct vb2_working_data *wd) +{ + return wd->selected_region.size == 0; +} + +void vboot_reboot(void) +{ + hard_reset(); +} +#endif diff --git a/src/vendorcode/google/chromeos/chromeos.h b/src/vendorcode/google/chromeos/chromeos.h index 724ff57893..14e0b10d18 100644 --- a/src/vendorcode/google/chromeos/chromeos.h +++ b/src/vendorcode/google/chromeos/chromeos.h @@ -47,6 +47,38 @@ struct romstage_handoff; /* TODO(shawnn): Remove these CONFIGs and define default weak functions * that can be overridden in the platform / MB code. */ #if CONFIG_VBOOT_VERIFY_FIRMWARE || CONFIG_VBOOT2_VERIFY_FIRMWARE +struct vboot_region { + uintptr_t offset_addr; + int32_t size; +}; + +/* + * 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 + +/* 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. */ +struct vboot_component_entry { + uint32_t offset; + uint32_t size; +} __attribute__((packed)); + +struct vboot_components { + uint32_t num_components; + struct vboot_component_entry entries[0]; +} __attribute__((packed)); + +void vboot_locate_region(const char *name, struct vboot_region *region); + +struct vboot_components *vboot_locate_components(struct vboot_region *region); + /* * This is a dual purpose routine. If dest is non-NULL the region at * offset_addr will be read into the area pointed to by dest. If dest @@ -87,8 +119,31 @@ static inline void chromeos_reserve_ram_oops(struct device *dev, int idx) {} #endif /* CONFIG_CHROMEOS_RAMOOPS */ #if CONFIG_VBOOT2_VERIFY_FIRMWARE -void select_firmware(void); -void vboot_create_handoff(void * vboot_workbuf); -#endif +void *vboot_load_ramstage(void); +void vboot2_verify_firmware(void); +void verstage_main(void); +void *vboot_load_stage(int stage_index, + struct vboot_region *fw_main, + struct vboot_components *fw_info); +void vboot_reboot(void); + +/* + * this is placed at the start of the vboot work buffer. selected_region is used + * for the verstage to return the location of the selected slot. buffer is used + * by the vboot2 core. + * + * TODO: Make the sizes of the struct and its members independent of cpu + * architectures as it crosses stage boundaries. + */ +struct vb2_working_data { + struct vboot_region selected_region; + size_t buffer_size; + uint8_t *buffer; +}; + +struct vb2_working_data * const vboot_get_working_data(void); +int vboot_is_slot_selected(struct vb2_working_data *wd); +int vboot_is_readonly_path(struct vb2_working_data *wd); +#endif /* CONFIG_VBOOT2_VERIFY_FIRMWARE */ #endif diff --git a/src/vendorcode/google/chromeos/vboot_context.h b/src/vendorcode/google/chromeos/vboot_context.h index cd82aacf0a..2517f63e63 100644 --- a/src/vendorcode/google/chromeos/vboot_context.h +++ b/src/vendorcode/google/chromeos/vboot_context.h @@ -21,21 +21,12 @@ #include <stdint.h> #include <vboot_api.h> +#include "chromeos.h" struct cbmem_entry; /* 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_region { - /* - * The offset_addr field may be an offset or an address. It depends - * on the capabilities of the underlying architecture. - */ - uintptr_t offset_addr; - int32_t size; -}; - struct vboot_context { struct vboot_handoff *handoff; VbCommonParams *cparams; diff --git a/src/vendorcode/google/chromeos/vboot_handoff.c b/src/vendorcode/google/chromeos/vboot_handoff.c index 3b276aac95..ae3096aee2 100644 --- a/src/vendorcode/google/chromeos/vboot_handoff.c +++ b/src/vendorcode/google/chromeos/vboot_handoff.c @@ -19,6 +19,8 @@ #include <2recovery_reasons.h> #include <2struct.h> +#include <arch/stages.h> +#include <assert.h> #include <stdint.h> #include <stddef.h> #include <string.h> @@ -33,13 +35,33 @@ #include "vboot_handoff.h" #include <vboot_struct.h> +static void *load_ramstage(struct vboot_handoff *vboot_handoff, + struct vboot_region *fw_main) +{ + struct vboot_components *fw_info; + 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; + } + + return vboot_load_stage(CONFIG_VBOOT_RAMSTAGE_INDEX, fw_main, fw_info); +} + /** * Sets vboot_handoff based on the information in vb2_shared_data * * TODO: Read wp switch to set VBSD_BOOT_FIRMWARE_WP_ENABLED */ static void fill_vboot_handoff(struct vboot_handoff *vboot_handoff, - struct vb2_shared_data *vb2_sd) + struct vb2_shared_data *vb2_sd) { VbSharedDataHeader *vb_sd = (VbSharedDataHeader *)vboot_handoff->shared_data; @@ -49,7 +71,6 @@ static void fill_vboot_handoff(struct vboot_handoff *vboot_handoff, vboot_handoff->selected_firmware = vb2_sd->fw_slot; - /* TODO: fw_slot is never 0xff while firmware_index can. */ vb_sd->firmware_index = vb2_sd->fw_slot; vb_sd->magic = VB_SHARED_DATA_MAGIC; @@ -61,7 +82,7 @@ static void fill_vboot_handoff(struct vboot_handoff *vboot_handoff, if (vb2_sd->recovery_reason) { vb_sd->firmware_index = 0xFF; if (vb2_sd->recovery_reason == VB2_RECOVERY_RO_MANUAL) - vb_sd->flags |= VBSD_BOOT_REC_SWITCH_ON; + vb_sd->flags |= VBSD_BOOT_REC_SWITCH_ON; *oflags |= VB_INIT_OUT_ENABLE_RECOVERY; *oflags |= VB_INIT_OUT_CLEAR_RAM; *oflags |= VB_INIT_OUT_ENABLE_DISPLAY; @@ -75,6 +96,7 @@ static void fill_vboot_handoff(struct vboot_handoff *vboot_handoff, vb_sd->flags |= VBSD_BOOT_DEV_SWITCH_ON; vb_sd->flags |= VBSD_LF_DEV_SWITCH_ON; } + /* TODO: Set these in depthcharge */ if (CONFIG_VIRTUAL_DEV_SWITCH) vb_sd->flags |= VBSD_HONOR_VIRT_DEV_SWITCH; if (CONFIG_EC_SOFTWARE_SYNC) { @@ -92,51 +114,58 @@ static void fill_vboot_handoff(struct vboot_handoff *vboot_handoff, if (vb2_sd->workbuf_preamble_size) { struct vb2_fw_preamble *fp; uintptr_t dst, src; - printk(BIOS_ERR, "Copying FW preamble\n"); + printk(BIOS_INFO, "Copying FW preamble\n"); fp = (struct vb2_fw_preamble *)( (uintptr_t)vb2_sd + vb2_sd->workbuf_preamble_offset); - src = (uintptr_t)&fp->kernel_subkey + fp->kernel_subkey.key_offset; + src = (uintptr_t)&fp->kernel_subkey + + fp->kernel_subkey.key_offset; dst = (uintptr_t)vb_sd + sizeof(VbSharedDataHeader); - memcpy((void *)dst, (void *)src, fp->kernel_subkey.key_size); - vb_sd->data_used += fp->kernel_subkey.key_size; + assert(dst + fp->kernel_subkey.key_size <= + (uintptr_t)vboot_handoff + sizeof(*vboot_handoff)); + memcpy((void *)dst, (void *)src, + fp->kernel_subkey.key_size); + vb_sd->data_used += fp->kernel_subkey.key_size; vb_sd->kernel_subkey.key_offset = dst - (uintptr_t)&vb_sd->kernel_subkey; vb_sd->kernel_subkey.key_size = fp->kernel_subkey.key_size; vb_sd->kernel_subkey.algorithm = fp->kernel_subkey.algorithm; - vb_sd->kernel_subkey.key_version = fp->kernel_subkey.key_version; + vb_sd->kernel_subkey.key_version = + fp->kernel_subkey.key_version; } vb_sd->recovery_reason = vb2_sd->recovery_reason; } /** - * Create vboot handoff struct - * - * 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)); + * Load ramstage and return the entry point */ -void vboot_create_handoff(void *vboot_workbuf) +void *vboot_load_ramstage(void) { struct vboot_handoff *vh; struct vb2_shared_data *sd; + struct vb2_working_data *wd = vboot_get_working_data(); - sd = (struct vb2_shared_data *)vboot_workbuf; + sd = (struct vb2_shared_data *)wd->buffer; sd->workbuf_hash_offset = 0; sd->workbuf_hash_size = 0; - printk(BIOS_INFO, "Creating vboot_handoff structure\n"); + printk(BIOS_INFO, "creating vboot_handoff structure\n"); vh = cbmem_add(CBMEM_ID_VBOOT_HANDOFF, sizeof(*vh)); - - if (vh == NULL) { - printk(BIOS_ERR, "Could not add vboot_handoff structure\n"); - return; - } + if (vh == NULL) + /* we don't need to failover gracefully here because this + * shouldn't happen with the image that has passed QA. */ + die("failed to allocate vboot_handoff structure\n"); memset(vh, 0, sizeof(*vh)); + /* 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; + + printk(BIOS_INFO, + "loading ramstage from Slot %c\n", sd->fw_slot ? 'B' : 'A'); + return load_ramstage(vh, &wd->selected_region); } diff --git a/src/vendorcode/google/chromeos/vboot_handoff.h b/src/vendorcode/google/chromeos/vboot_handoff.h index e1e519f081..b0017438ac 100644 --- a/src/vendorcode/google/chromeos/vboot_handoff.h +++ b/src/vendorcode/google/chromeos/vboot_handoff.h @@ -21,16 +21,7 @@ #include <vboot_api.h> #include <vboot_struct.h> - -/* - * 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 +#include "chromeos.h" struct firmware_component { uint32_t address; diff --git a/src/vendorcode/google/chromeos/vboot_loader.c b/src/vendorcode/google/chromeos/vboot_loader.c index 0294ac63ac..2a1ee3e7a7 100644 --- a/src/vendorcode/google/chromeos/vboot_loader.c +++ b/src/vendorcode/google/chromeos/vboot_loader.c @@ -34,26 +34,9 @@ #include <stdlib.h> #include <timestamp.h> #include "chromeos.h" -#include "fmap.h" #include "vboot_context.h" #include "vboot_handoff.h" -/* 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. */ - -struct component_entry { - uint32_t offset; - uint32_t size; -} __attribute__((packed)); - -struct components { - uint32_t num_components; - struct component_entry entries[0]; -} __attribute__((packed)); - - #define TEMP_CBMEM_ID_VBOOT 0xffffffff #define TEMP_CBMEM_ID_VBLOCKS 0xfffffffe @@ -93,24 +76,13 @@ static void fatal_error(void) hard_reset(); } -static void locate_region(const char *name, struct vboot_region *region) -{ - region->size = find_fmap_entry(name, (void **)®ion->offset_addr); -} - static int fw_region_size(struct vboot_region *r) { - struct components *fw_info; + struct vboot_components *fw_info; int32_t size; - size_t req_size; int i; - req_size = sizeof(*fw_info); - req_size += sizeof(struct component_entry) * MAX_PARSED_FW_COMPONENTS; - - /* This will leak a mapping. */ - fw_info = vboot_get_region(r->offset_addr, req_size, NULL); - + fw_info = vboot_locate_components(r); if (fw_info == NULL) return -1; @@ -118,7 +90,7 @@ static int fw_region_size(struct vboot_region *r) return -1; size = sizeof(*fw_info); - size += sizeof(struct component_entry) * fw_info->num_components; + size += sizeof(struct vboot_component_entry) * fw_info->num_components; for (i = 0; i < fw_info->num_components; i++) size += ALIGN(fw_info->entries[i].size, sizeof(uint32_t)); @@ -190,9 +162,8 @@ static int vboot_fill_params(struct vboot_context *ctx) static void fill_handoff(struct vboot_context *context) { - struct components *fw_info; + struct vboot_components *fw_info; struct vboot_region *region; - size_t req_size; int i; /* Fix up the handoff structure. */ @@ -207,12 +178,7 @@ static void fill_handoff(struct vboot_context *context) else return; - req_size = sizeof(*fw_info); - req_size += sizeof(struct component_entry) * MAX_PARSED_FW_COMPONENTS; - - /* This will leak a mapping. */ - fw_info = vboot_get_region(region->offset_addr, req_size, NULL); - + fw_info = vboot_locate_components(region); if (fw_info == NULL) return; @@ -273,11 +239,11 @@ static void vboot_invoke_wrapper(struct vboot_handoff *vboot_handoff) cparams.shared_data_size = VB_SHARED_DATA_MIN_SIZE; cparams.caller_context = &context; - locate_region("GBB", &context.gbb); - locate_region("VBLOCK_A", &context.vblock_a); - locate_region("VBLOCK_B", &context.vblock_b); - locate_region("FW_MAIN_A", &context.fw_a); - locate_region("FW_MAIN_B", &context.fw_b); + vboot_locate_region("GBB", &context.gbb); + vboot_locate_region("VBLOCK_A", &context.vblock_a); + vboot_locate_region("VBLOCK_B", &context.vblock_b); + vboot_locate_region("FW_MAIN_A", &context.fw_a); + vboot_locate_region("FW_MAIN_B", &context.fw_b); /* Check all fmap entries. */ if (context.fw_a.size < 0 || context.fw_b.size < 0 || diff --git a/src/vendorcode/google/chromeos/verstage.c b/src/vendorcode/google/chromeos/verstage.c index 1564d8031b..4970124b04 100644 --- a/src/vendorcode/google/chromeos/verstage.c +++ b/src/vendorcode/google/chromeos/verstage.c @@ -1,58 +1,38 @@ +/* + * 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 <2api.h> #include <2struct.h> #include <antirollback.h> -#include <arch/exception.h> -#include <arch/stages.h> -#include <soc/nvidia/tegra124/cache.h> -#include <cbfs.h> #include <console/console.h> #include <console/vtxprintf.h> -#include <reset.h> -#include <soc/addressmap.h> -#include <soc/clock.h> #include <string.h> #include "chromeos.h" -#include "fmap.h" - -#define VBDEBUG(format, args...) \ - printk(BIOS_INFO, "%s():%d: " format, __func__, __LINE__, ## args) -#define TODO_BLOCK_SIZE 8192 -#define MAX_PARSED_FW_COMPONENTS 5 -#define ROMSTAGE_INDEX 2 - -struct component_entry { - uint32_t offset; - uint32_t size; -} __attribute__((packed)); - -struct components { - uint32_t num_components; - struct component_entry entries[MAX_PARSED_FW_COMPONENTS]; -} __attribute__((packed)); - -struct vboot_region { - uintptr_t offset_addr; - int32_t size; -}; - -static void locate_region(const char *name, struct vboot_region *region) -{ - region->size = find_fmap_entry(name, (void **)®ion->offset_addr); - VBDEBUG("Located %s @%x\n", name, region->offset_addr); -} + +#define TODO_BLOCK_SIZE 1024 static int is_slot_a(struct vb2_context *ctx) { return !(ctx->flags & VB2_CONTEXT_FW_SLOT_B); } -static int in_ro(void) -{ - /* TODO: Implement */ - return 1; -} - /* exports */ void vb2ex_printf(const char *func, const char *fmt, ...) @@ -61,7 +41,7 @@ void vb2ex_printf(const char *func, const char *fmt, ...) printk(BIOS_INFO, "VB2:%s() ", func); va_start(args, fmt); - printk(BIOS_INFO, fmt, args); + vprintk(BIOS_INFO, fmt, args); va_end(args); return; @@ -70,7 +50,7 @@ void vb2ex_printf(const char *func, const char *fmt, ...) int vb2ex_tpm_clear_owner(struct vb2_context *ctx) { uint32_t rv; - VBDEBUG("Clearing TPM owner\n"); + printk(BIOS_INFO, "Clearing TPM owner\n"); rv = tpm_clear_and_reenable(); if (rv) return VB2_ERROR_EX_TPM_CLEAR_OWNER; @@ -87,13 +67,13 @@ int vb2ex_read_resource(struct vb2_context *ctx, switch (index) { case VB2_RES_GBB: - locate_region("GBB", ®ion); + vboot_locate_region("GBB", ®ion); break; case VB2_RES_FW_VBLOCK: if (is_slot_a(ctx)) - locate_region("VBLOCK_A", ®ion); + vboot_locate_region("VBLOCK_A", ®ion); else - locate_region("VBLOCK_B", ®ion); + vboot_locate_region("VBLOCK_B", ®ion); break; default: return VB2_ERROR_EX_READ_RESOURCE_INDEX; @@ -108,29 +88,10 @@ int vb2ex_read_resource(struct vb2_context *ctx, return VB2_SUCCESS; } -static void reboot(void) -{ - cpu_reset(); -} - -static void recovery(void) -{ - void *entry; - - if (!in_ro()) - reboot(); - - entry = cbfs_load_stage(CBFS_DEFAULT_MEDIA, "fallback/romstage"); - if (entry != (void *)-1) - stage_exit(entry); - - for (;;); -} - static int hash_body(struct vb2_context *ctx, struct vboot_region *fw_main) { uint32_t expected_size; - uint8_t block[TODO_BLOCK_SIZE]; + MAYBE_STATIC uint8_t block[TODO_BLOCK_SIZE]; size_t block_size = sizeof(block); uintptr_t offset; int rv; @@ -168,122 +129,57 @@ static int hash_body(struct vb2_context *ctx, struct vboot_region *fw_main) return VB2_SUCCESS; } -static int locate_fw_components(struct vb2_context *ctx, - struct vboot_region *fw_main, - struct components *fw_info) +static int locate_firmware(struct vb2_context *ctx, + struct vboot_region *fw_main) { if (is_slot_a(ctx)) - locate_region("FW_MAIN_A", fw_main); + vboot_locate_region("FW_MAIN_A", fw_main); else - locate_region("FW_MAIN_B", fw_main); + vboot_locate_region("FW_MAIN_B", fw_main); + if (fw_main->size < 0) return 1; - if (vboot_get_region(fw_main->offset_addr, - sizeof(*fw_info), fw_info) == NULL) - return 1; return 0; } -static struct cbfs_stage *load_stage(struct vb2_context *ctx, - int stage_index, - struct vboot_region *fw_main, - struct components *fw_info) -{ - struct cbfs_stage *stage; - uint32_t fc_addr; - uint32_t fc_size; - - /* Check for invalid address. */ - fc_addr = fw_main->offset_addr + fw_info->entries[stage_index].offset; - fc_size = fw_info->entries[stage_index].size; - if (fc_addr == 0 || fc_size == 0) { - VBDEBUG("romstage address invalid.\n"); - return NULL; - } - - /* Loading to cbfs cache. This stage data must be retained until it's - * decompressed. */ - stage = vboot_get_region(fc_addr, fc_size, NULL); - - if (stage == NULL) { - VBDEBUG("Unable to load a stage.\n"); - return NULL; - } - - return stage; -} - -static void enter_stage(struct cbfs_stage *stage) -{ - /* Stages rely the below clearing so that the bss is initialized. */ - memset((void *) (uintptr_t)stage->load, 0, stage->memlen); - - if (cbfs_decompress(stage->compression, - (unsigned char *)stage + sizeof(*stage), - (void *) (uintptr_t) stage->load, - stage->len)) - return; - - VBDEBUG("Jumping to entry @%llx.\n", stage->entry); - stage_exit((void *)(uintptr_t)stage->entry); -} - -static void enable_cache(void) -{ - mmu_init(); - mmu_config_range(0, CONFIG_SYS_SDRAM_BASE >> 20, DCACHE_OFF); - mmu_config_range(0x40000000 >> 20, 2, DCACHE_WRITEBACK); - mmu_disable_range(0, 1); - VBDEBUG("Enabling cache\n"); - dcache_mmu_enable(); -} - /** * Save non-volatile and/or secure data if needed. */ static void save_if_needed(struct vb2_context *ctx) { if (ctx->flags & VB2_CONTEXT_NVDATA_CHANGED) { - VBDEBUG("Saving nvdata\n"); + printk(BIOS_INFO, "Saving nvdata\n"); save_vbnv(ctx->nvdata); ctx->flags &= ~VB2_CONTEXT_NVDATA_CHANGED; } if (ctx->flags & VB2_CONTEXT_SECDATA_CHANGED) { - VBDEBUG("Saving secdata\n"); + printk(BIOS_INFO, "Saving secdata\n"); antirollback_write_space_firmware(ctx); ctx->flags &= ~VB2_CONTEXT_SECDATA_CHANGED; } } /** - * Load and verify the next stage from RW image and jump to it - * - * If validation fails, it exits to romstage for recovery or reboots. + * Verify and select the firmware in the RW image * * TODO: Avoid loading a stage twice (once in hash_body & again in load_stage). * when per-stage verification is ready. */ -void __attribute__((noinline)) select_firmware(void) +#if CONFIG_RETURN_FROM_VERSTAGE +void main(void) +#else +void verstage_main(void) +#endif /* CONFIG_RETURN_FROM_VERSTAGE */ { struct vb2_context ctx; - uint8_t *workbuf = (uint8_t *)CONFIG_VBOOT_WORK_BUFFER_ADDRESS; - struct vboot_region fw_main; - struct components fw_info; - struct cbfs_stage *stage; + struct vb2_working_data *wd = vboot_get_working_data(); int rv; - /* Do minimum to enable cache and run vboot at full speed */ - configure_l2_cache(); - console_init(); - exception_init(); - enable_cache(); - - /* Set up context */ + /* Set up context and work buffer */ memset(&ctx, 0, sizeof(ctx)); - ctx.workbuf = workbuf; - ctx.workbuf_size = CONFIG_VBOOT_WORK_BUFFER_SIZE; - memset(ctx.workbuf, 0, ctx.workbuf_size); + ctx.workbuf = wd->buffer; + ctx.workbuf_size = wd->buffer_size; /* Read nvdata from a non-volatile storage */ read_vbnv(ctx.nvdata); @@ -301,59 +197,53 @@ void __attribute__((noinline)) select_firmware(void) } /* Do early init */ - VBDEBUG("Phase 1\n"); + printk(BIOS_INFO, "Phase 1\n"); rv = vb2api_fw_phase1(&ctx); if (rv) { - VBDEBUG("Recovery requested (%x)\n", rv); + printk(BIOS_INFO, "Recovery requested (%x)\n", rv); /* If we need recovery mode, leave firmware selection now */ save_if_needed(&ctx); - recovery(); + return; } /* Determine which firmware slot to boot */ - VBDEBUG("Phase 2\n"); + printk(BIOS_INFO, "Phase 2\n"); rv = vb2api_fw_phase2(&ctx); if (rv) { - VBDEBUG("Reboot requested (%x)\n", rv); + printk(BIOS_INFO, "Reboot requested (%x)\n", rv); save_if_needed(&ctx); - reboot(); + vboot_reboot(); } /* Try that slot */ - VBDEBUG("Phase 3\n"); + printk(BIOS_INFO, "Phase 3\n"); rv = vb2api_fw_phase3(&ctx); if (rv) { - VBDEBUG("Reboot requested (%x)\n", rv); + printk(BIOS_INFO, "Reboot requested (%x)\n", rv); save_if_needed(&ctx); - reboot(); + vboot_reboot(); } - VBDEBUG("Phase 4\n"); - rv = locate_fw_components(&ctx, &fw_main, &fw_info); - if (rv) { - VBDEBUG("Failed to locate firmware components\n"); - reboot(); - } - rv = hash_body(&ctx, &fw_main); - stage = load_stage(&ctx, ROMSTAGE_INDEX, &fw_main, &fw_info); - if (stage == NULL) { - VBDEBUG("Failed to load stage\n"); - reboot(); - } + printk(BIOS_INFO, "Phase 4\n"); + rv = locate_firmware(&ctx, &wd->selected_region); + if (rv) + die("Failed to read FMAP to locate firmware"); + + rv = hash_body(&ctx, &wd->selected_region); save_if_needed(&ctx); if (rv) { - VBDEBUG("Reboot requested (%x)\n", rv); - reboot(); + printk(BIOS_INFO, "Reboot requested (%x)\n", rv); + vboot_reboot(); } - /* TODO: Do we need to lock secdata? */ - VBDEBUG("Locking TPM\n"); - - /* Load next stage and jump to it */ - VBDEBUG("Jumping to rw-romstage @%llx\n", stage->entry); - enter_stage(stage); + /* Lock TPM */ + rv = antirollback_lock_space_firmware(); + if (rv) { + printk(BIOS_INFO, "Failed to lock TPM (%x)\n", rv); + vb2api_fail(&ctx, VB2_RECOVERY_RO_TPM_L_ERROR, 0); + save_if_needed(&ctx); + vboot_reboot(); + } - /* Shouldn't reach here */ - VBDEBUG("Halting\n"); - for (;;); + printk(BIOS_INFO, "Slot %c is selected\n", is_slot_a(&ctx) ? 'A' : 'B'); } diff --git a/src/vendorcode/google/chromeos/verstub.c b/src/vendorcode/google/chromeos/verstub.c new file mode 100644 index 0000000000..5df1777bb2 --- /dev/null +++ b/src/vendorcode/google/chromeos/verstub.c @@ -0,0 +1,93 @@ +/* + * 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 "chromeos.h" + +static struct vb2_working_data *init_vb2_working_data(void) +{ + struct vb2_working_data *wd; + + wd = vboot_get_working_data(); + memset(wd, 0, CONFIG_VBOOT_WORK_BUFFER_SIZE); + /* 8-byte alignment for ARMv7 */ + wd->buffer = (uint8_t *)ALIGN_UP((uintptr_t)&wd[1], 8); + wd->buffer_size = CONFIG_VBOOT_WORK_BUFFER_SIZE + (uintptr_t)wd + - (uintptr_t)wd->buffer; + + 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 CONFIG_RETURN_FROM_VERSTAGE + /* load verstage from RO */ + entry = cbfs_load_stage(CBFS_DEFAULT_MEDIA, + CONFIG_CBFS_PREFIX "/verstage"); + if (entry == -1) + die("failed to load verstage"); + + /* verify and select a slot */ + stage_exit(entry); +#else + verstage_main(); +#endif /* CONFIG_RETURN_FROM_VERSTAGE */ + + /* jump to the selected slot */ + entry = NULL; + if (vboot_is_slot_selected(wd)) { + /* RW A or B */ + struct vboot_components *fw_info = + vboot_locate_components(&wd->selected_region); + if (fw_info == NULL) + die("failed to locate firmware components\n"); + entry = vboot_load_stage(CONFIG_VBOOT_ROMSTAGE_INDEX, + &wd->selected_region, fw_info); + } else if (vboot_is_readonly_path(wd)) { + /* RO */ + entry = cbfs_load_stage(CBFS_DEFAULT_MEDIA, + CONFIG_CBFS_PREFIX "/romstage"); + } + + if (entry != NULL && entry != (void *)-1) + stage_exit(entry); + + die("failed to exit from stage\n"); +} |