summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Durbin <adurbin@chromium.org>2014-03-20 14:28:17 -0500
committerMarc Jones <marc.jones@se-eng.com>2014-12-09 18:41:00 +0100
commit30974bc2f5f4764425e69256782cca03b290c4f4 (patch)
tree43643987aeb0056ce5e5e154f464e856dc352919
parentf72f9e7c14a2a5df92c30a3ec88d15b9aca30b1e (diff)
downloadcoreboot-30974bc2f5f4764425e69256782cca03b290c4f4.tar.xz
vboot: allow for non-memory-mapped VBOOT regions
Depending on the platform the underlying regions vboot requires may not be accessible through a memory-mapped interface. Allow for non-memory-mapped regions by providing a region request abstraction. There is then only a few touch points in the code to provide compile-time decision making no how to obtain a region. For the vblocks a temporary area is allocated from cbmem. They are then read from the SPI into the temporarily buffer. BUG=chrome-os-partner:27094 BRANCH=None TEST=Built and booted a rambi with vboot verification. Original-Change-Id: I828a7c36387a8eb573c5a0dd020fe9abad03d902 Original-Signed-off-by: Aaron Durbin <adurbin@chromium.org> Original-Reviewed-on: https://chromium-review.googlesource.com/190924 Original-Reviewed-by: Hung-Te Lin <hungte@chromium.org> (cherry picked from commit aee0280bbfe110eae88aa297b433c1038c6fe8a3) Signed-off-by: Marc Jones <marc.jones@se-eng.com> Change-Id: Ia020d1eebad753da950342656cd11b84e9a85376 Reviewed-on: http://review.coreboot.org/7709 Tested-by: build bot (Jenkins) Reviewed-by: Patrick Georgi <pgeorgi@google.com>
-rw-r--r--src/drivers/spi/Kconfig7
-rw-r--r--src/vendorcode/google/chromeos/Makefile.inc5
-rw-r--r--src/vendorcode/google/chromeos/chromeos.h7
-rw-r--r--src/vendorcode/google/chromeos/vboot_context.h22
-rw-r--r--src/vendorcode/google/chromeos/vboot_handoff.c29
-rw-r--r--src/vendorcode/google/chromeos/vboot_loader.c194
-rw-r--r--src/vendorcode/google/chromeos/vboot_wrapper.c124
7 files changed, 296 insertions, 92 deletions
diff --git a/src/drivers/spi/Kconfig b/src/drivers/spi/Kconfig
index 4cc439ac8f..f96bf9f88b 100644
--- a/src/drivers/spi/Kconfig
+++ b/src/drivers/spi/Kconfig
@@ -26,6 +26,13 @@ config SPI_FLASH
if SPI_FLASH
+config SPI_FLASH_MEMORY_MAPPED
+ bool
+ default y if ARCH_X86
+ default n if !ARCH_X86
+ help
+ Inform system if SPI is memory-mapped or not.
+
config SPI_FLASH_SMM
bool "SPI flash driver support in SMM"
default n
diff --git a/src/vendorcode/google/chromeos/Makefile.inc b/src/vendorcode/google/chromeos/Makefile.inc
index d02c09ff5e..678c4ee97c 100644
--- a/src/vendorcode/google/chromeos/Makefile.inc
+++ b/src/vendorcode/google/chromeos/Makefile.inc
@@ -43,6 +43,10 @@ ramstage-y += vboot_handoff.c
romstage-y += vboot_loader.c
rmodules_$(ARCH-romstage-y)-y += vboot_wrapper.c
+ifneq ($(CONFIG_SPI_FLASH_MEMORY_MAPPED),y)
+VBOOT_MAKEFLAGS = REGION_READ=1
+endif
+
VB_LIB = $(obj)/external/vboot_reference/vboot_fw.a
# Currently, vboot comes into picture only during the romstage, thus
# is compiled for being used in romstage only. Since, we are splitting
@@ -83,6 +87,7 @@ $(VB_LIB):
CC="$(CC_romstage)" \
CFLAGS="$(VBOOT_CFLAGS)" \
make -C $(VB_SOURCE) \
+ $(VBOOT_MAKEFLAGS) \
BUILD=../$(dir $(VB_LIB)) \
V=$(V) \
fwlib
diff --git a/src/vendorcode/google/chromeos/chromeos.h b/src/vendorcode/google/chromeos/chromeos.h
index 20cac997ed..c162891fdd 100644
--- a/src/vendorcode/google/chromeos/chromeos.h
+++ b/src/vendorcode/google/chromeos/chromeos.h
@@ -36,6 +36,13 @@ void init_chromeos(int bootmode);
struct romstage_handoff;
#if CONFIG_VBOOT_VERIFY_FIRMWARE
+/*
+ * 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
+ * is NULL,the region will be mapped to a memory location. NULL is
+ * returned on error else the location of the requested region.
+ */
+void *vboot_get_region(uintptr_t offset_addr, size_t size, void *dest);
/* Returns 0 on success < 0 on error. */
int vboot_get_handoff_info(void **addr, uint32_t *size);
int vboot_enable_developer(void);
diff --git a/src/vendorcode/google/chromeos/vboot_context.h b/src/vendorcode/google/chromeos/vboot_context.h
index 72a05350cd..822fed4d2e 100644
--- a/src/vendorcode/google/chromeos/vboot_context.h
+++ b/src/vendorcode/google/chromeos/vboot_context.h
@@ -22,17 +22,30 @@
#include <stdint.h>
#include <vboot_api.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;
VbSelectFirmwareParams *fparams;
- uint8_t *fw_a;
- uint32_t fw_a_size;
- uint8_t *fw_b;
- uint32_t fw_b_size;
+ struct vboot_region gbb;
+ struct vboot_region vblock_a;
+ struct vboot_region fw_a;
+ struct vboot_region vblock_b;
+ struct vboot_region fw_b;
+ const struct cbmem_entry *vblocks;
/* Callback implementations living in romstage. */
void (*read_vbnv)(uint8_t *vbnv_copy);
void (*save_vbnv)(const uint8_t *vbnv_copy);
@@ -43,6 +56,7 @@ struct vboot_context {
size_t *recv_len);
void (*log_msg)(const char *fmt, va_list args);
void (*fatal_error)(void);
+ void *(*get_region)(uintptr_t offset_addr, size_t size, void *dest);
};
#endif /* VBOOT_CONTEXT_H */
diff --git a/src/vendorcode/google/chromeos/vboot_handoff.c b/src/vendorcode/google/chromeos/vboot_handoff.c
index 937b2e31c0..c3c5a138d7 100644
--- a/src/vendorcode/google/chromeos/vboot_handoff.c
+++ b/src/vendorcode/google/chromeos/vboot_handoff.c
@@ -18,8 +18,10 @@
*/
#include <stddef.h>
+#include <string.h>
#include "chromeos.h"
#include <boot/coreboot_tables.h>
+#include <cbfs.h>
#include <cbmem.h>
#include <console/console.h>
#include <payload_loader.h>
@@ -52,6 +54,33 @@ int vboot_enable_recovery(void)
return !!(vbho->init_params.out_flags & VB_INIT_OUT_ENABLE_RECOVERY);
}
+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;
+ }
+}
+
static void *vboot_get_payload(size_t *len)
{
struct vboot_handoff *vboot_handoff;
diff --git a/src/vendorcode/google/chromeos/vboot_loader.c b/src/vendorcode/google/chromeos/vboot_loader.c
index cfdc5af7df..8e0babed35 100644
--- a/src/vendorcode/google/chromeos/vboot_loader.c
+++ b/src/vendorcode/google/chromeos/vboot_loader.c
@@ -20,6 +20,7 @@
#include <arch/stages.h>
#include <stdint.h>
#include <stddef.h>
+#include <string.h>
#include <cbfs.h>
#include <cbmem.h>
#include <console/console.h>
@@ -37,10 +38,29 @@
#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
+
static void vboot_run_stub(struct vboot_context *context)
{
struct rmod_stage_load rmod_stage = {
- .cbmem_id = 0xffffffff,
+ .cbmem_id = TEMP_CBMEM_ID_VBOOT,
.name = CONFIG_CBFS_PREFIX "/vboot",
};
void (*entry)(struct vboot_context *context);
@@ -73,6 +93,142 @@ 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 **)&region->offset_addr);
+}
+
+static int fw_region_size(struct vboot_region *r)
+{
+ struct 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);
+
+ if (fw_info == NULL)
+ return -1;
+
+ if (fw_info->num_components > MAX_PARSED_FW_COMPONENTS)
+ return -1;
+
+ size = sizeof(*fw_info);
+ size += sizeof(struct 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));
+
+ /* Check that size of comopnents does not exceed the region's size. */
+ if (size > r->size)
+ return -1;
+
+ /* Update region with the correct size. */
+ r->size = size;
+
+ return 0;
+}
+
+static int vboot_fill_params(struct vboot_context *ctx)
+{
+ VbCommonParams *cparams;
+ VbSelectFirmwareParams *fparams;
+
+ if (fw_region_size(&ctx->fw_a))
+ return -1;
+
+ if (fw_region_size(&ctx->fw_b))
+ return -1;
+
+ cparams = ctx->cparams;
+ fparams = ctx->fparams;
+
+ cparams->gbb_size = ctx->gbb.size;
+ fparams->verification_size_A = ctx->vblock_a.size;
+ fparams->verification_size_B = ctx->vblock_b.size;
+
+ if (IS_ENABLED(CONFIG_SPI_FLASH_MEMORY_MAPPED)) {
+ /* Get memory-mapped pointers to the regions. */
+ cparams->gbb_data = vboot_get_region(ctx->gbb.offset_addr,
+ ctx->gbb.size, NULL);
+ fparams->verification_block_A =
+ vboot_get_region(ctx->vblock_a.offset_addr,
+ ctx->vblock_a.size, NULL);
+ fparams->verification_block_B =
+ vboot_get_region(ctx->vblock_b.offset_addr,
+ ctx->vblock_b.size, NULL);
+ } else {
+ /*
+ * Copy the vblock info into a buffer in cbmem. The gbb will
+ * be read using VbExRegionRead().
+ */
+ char *dest;
+ size_t vblck_sz;
+
+ vblck_sz = ctx->vblock_a.size + ctx->vblock_b.size;
+ ctx->vblocks = cbmem_entry_add(TEMP_CBMEM_ID_VBLOCKS, vblck_sz);
+ if (ctx->vblocks == NULL)
+ return -1;
+ dest = cbmem_entry_start(ctx->vblocks);
+ if (vboot_get_region(ctx->vblock_a.offset_addr,
+ ctx->vblock_a.size, dest) == NULL)
+ return -1;
+ fparams->verification_block_A = (void *)dest;
+ dest += ctx->vblock_a.size;
+ if (vboot_get_region(ctx->vblock_b.offset_addr,
+ ctx->vblock_b.size, dest) == NULL)
+ return -1;
+ fparams->verification_block_B = (void *)dest;
+ }
+
+ return 0;
+}
+
+static void fill_handoff(struct vboot_context *context)
+{
+ struct components *fw_info;
+ struct vboot_region *region;
+ size_t req_size;
+ int i;
+
+ /* 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)
+ region = &context->fw_a;
+ else if (context->handoff->selected_firmware == VB_SELECT_FIRMWARE_B)
+ region = &context->fw_b;
+ 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);
+
+ if (fw_info == NULL)
+ return;
+
+ for (i = 0; i < fw_info->num_components; i++) {
+ context->handoff->components[i].address =
+ region->offset_addr + fw_info->entries[i].offset;
+ context->handoff->components[i].size = fw_info->entries[i].size;
+ }
+}
+
+static void vboot_clean_up(struct vboot_context *context)
+{
+ if (context->vblocks != NULL)
+ cbmem_entry_remove(context->vblocks);
+}
+
static void vboot_invoke_wrapper(struct vboot_handoff *vboot_handoff)
{
VbCommonParams cparams;
@@ -104,30 +260,30 @@ static void vboot_invoke_wrapper(struct vboot_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);
+ 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);
/* 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) {
+ if (context.fw_a.size < 0 || context.fw_b.size < 0 ||
+ context.vblock_a.size < 0 || context.vblock_b.size < 0 ||
+ context.gbb.size < 0) {
printk(BIOS_DEBUG, "Not all fmap entries found for vboot.\n");
return;
}
+ /* Fill in vboot parameters. */
+ if (vboot_fill_params(&context)) {
+ vboot_clean_up(&context);
+ return;
+ }
+
/* Initialize callbacks. */
context.read_vbnv = &read_vbnv;
context.save_vbnv = &save_vbnv;
@@ -137,8 +293,13 @@ static void vboot_invoke_wrapper(struct vboot_handoff *vboot_handoff)
context.tis_sendrecv = &tis_sendrecv;
context.log_msg = &log_msg;
context.fatal_error = &fatal_error;
+ context.get_region = &vboot_get_region;
vboot_run_stub(&context);
+
+ fill_handoff(&context);
+
+ vboot_clean_up(&context);
}
#if CONFIG_RELOCATABLE_RAMSTAGE
@@ -228,7 +389,8 @@ static void vboot_load_ramstage(struct vboot_handoff *vboot_handoff,
printk(BIOS_DEBUG, "RW ramstage image at 0x%08x, 0x%08x bytes.\n",
fwc->address, fwc->size);
- stage = vboot_get_region(fwc->address, fwc->size);
+ /* This will leak a mapping. */
+ stage = vboot_get_region(fwc->address, fwc->size, NULL);
if (stage == NULL) {
printk(BIOS_DEBUG, "Unable to get RW ramstage region.\n");
diff --git a/src/vendorcode/google/chromeos/vboot_wrapper.c b/src/vendorcode/google/chromeos/vboot_wrapper.c
index d008de17cb..5611451654 100644
--- a/src/vendorcode/google/chromeos/vboot_wrapper.c
+++ b/src/vendorcode/google/chromeos/vboot_wrapper.c
@@ -31,43 +31,9 @@
/* 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(void *arg)
{
- int i;
VbError_t res;
- const struct components *components;
struct vboot_context *context;
context = arg;
@@ -86,23 +52,6 @@ static void vboot_wrapper(void *arg)
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, ...)
@@ -203,43 +152,51 @@ void *Memset(void *dest, const uint8_t c, uint64_t n)
return memset(dest, c, n);
}
+static inline size_t get_hash_block_size(size_t requested_size)
+{
+ if (!IS_ENABLED(CONFIG_SPI_FLASH_MEMORY_MAPPED)) {
+ const size_t block_size = 64 * 1024;
+ if (requested_size > block_size)
+ return block_size;
+ }
+ return requested_size;
+}
+
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;
+ struct vboot_region *region;
+ struct vboot_context *ctx;
+ size_t data_size;
+ uintptr_t offset_addr;
+
+ ctx = cparams->caller_context;
switch (firmware_index) {
case VB_SELECT_FIRMWARE_A:
- data = gcontext->fw_a;
- size = gcontext->fw_a_size;
+ region = &ctx->fw_a;
break;
case VB_SELECT_FIRMWARE_B:
- data = gcontext->fw_b;
- size = gcontext->fw_b_size;
+ region = &ctx->fw_b;
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);
+ data_size = region->size;
+ offset_addr = region->offset_addr;
+ while (data_size) {
+ size_t block_size;
- for (i = 0; i < components->num_components; i++)
- data_size += ALIGN(components->entries[i].size, 4);
-
- if (size < data_size)
+ block_size = get_hash_block_size(data_size);
+ data = ctx->get_region(offset_addr, block_size, NULL);
+ if (data == NULL)
return VBERROR_UNKNOWN;
+ VbUpdateFirmwareBodyHash(cparams, data, block_size);
- VbUpdateFirmwareBodyHash(cparams, data, data_size);
+ data_size -= block_size;
+ offset_addr += block_size;
+ }
return VBERROR_SUCCESS;
}
@@ -274,4 +231,27 @@ VbError_t VbExTpmSendReceive(const uint8_t *request, uint32_t request_length,
return VBERROR_SUCCESS;
}
+#if !CONFIG_SPI_FLASH_MEMORY_MAPPED
+VbError_t VbExRegionRead(VbCommonParams *cparams,
+ enum vb_firmware_region region, uint32_t offset,
+ uint32_t size, void *buf)
+{
+ struct vboot_context *ctx;
+ VbExDebug("VbExRegionRead: offset=%x size=%x, buf=%p\n",
+ offset, size, buf);
+ ctx = cparams->caller_context;
+
+ if (region == VB_REGION_GBB) {
+ if (offset + size > cparams->gbb_size)
+ return VBERROR_REGION_READ_INVALID;
+ offset += ctx->gbb.offset_addr;
+ if (ctx->get_region(offset, size, buf) == NULL)
+ return VBERROR_REGION_READ_INVALID;
+ return VBERROR_SUCCESS;
+ }
+
+ return VBERROR_UNSUPPORTED_REGION;
+}
+#endif /* CONFIG_SPI_FLASH_MEMORY_MAPPED */
+
RMODULE_ENTRY(vboot_wrapper);