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>
This commit is contained in:
Aaron Durbin 2014-03-20 14:28:17 -05:00 committed by Marc Jones
parent f72f9e7c14
commit 30974bc2f5
7 changed files with 296 additions and 92 deletions

View File

@ -26,6 +26,13 @@ config SPI_FLASH
if 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 config SPI_FLASH_SMM
bool "SPI flash driver support in SMM" bool "SPI flash driver support in SMM"
default n default n

View File

@ -43,6 +43,10 @@ ramstage-y += vboot_handoff.c
romstage-y += vboot_loader.c romstage-y += vboot_loader.c
rmodules_$(ARCH-romstage-y)-y += vboot_wrapper.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 VB_LIB = $(obj)/external/vboot_reference/vboot_fw.a
# Currently, vboot comes into picture only during the romstage, thus # Currently, vboot comes into picture only during the romstage, thus
# is compiled for being used in romstage only. Since, we are splitting # is compiled for being used in romstage only. Since, we are splitting
@ -83,6 +87,7 @@ $(VB_LIB):
CC="$(CC_romstage)" \ CC="$(CC_romstage)" \
CFLAGS="$(VBOOT_CFLAGS)" \ CFLAGS="$(VBOOT_CFLAGS)" \
make -C $(VB_SOURCE) \ make -C $(VB_SOURCE) \
$(VBOOT_MAKEFLAGS) \
BUILD=../$(dir $(VB_LIB)) \ BUILD=../$(dir $(VB_LIB)) \
V=$(V) \ V=$(V) \
fwlib fwlib

View File

@ -36,6 +36,13 @@ void init_chromeos(int bootmode);
struct romstage_handoff; struct romstage_handoff;
#if CONFIG_VBOOT_VERIFY_FIRMWARE #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. */ /* Returns 0 on success < 0 on error. */
int vboot_get_handoff_info(void **addr, uint32_t *size); int vboot_get_handoff_info(void **addr, uint32_t *size);
int vboot_enable_developer(void); int vboot_enable_developer(void);

View File

@ -22,17 +22,30 @@
#include <stdint.h> #include <stdint.h>
#include <vboot_api.h> #include <vboot_api.h>
struct cbmem_entry;
/* The vboot context structure provides all the necessary data for invoking /* The vboot context structure provides all the necessary data for invoking
* vboot. The vboot loader sets everything up for vboot module to use. */ * 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_context {
struct vboot_handoff *handoff; struct vboot_handoff *handoff;
VbCommonParams *cparams; VbCommonParams *cparams;
VbSelectFirmwareParams *fparams; VbSelectFirmwareParams *fparams;
uint8_t *fw_a; struct vboot_region gbb;
uint32_t fw_a_size; struct vboot_region vblock_a;
uint8_t *fw_b; struct vboot_region fw_a;
uint32_t fw_b_size; struct vboot_region vblock_b;
struct vboot_region fw_b;
const struct cbmem_entry *vblocks;
/* Callback implementations living in romstage. */ /* Callback implementations living in romstage. */
void (*read_vbnv)(uint8_t *vbnv_copy); void (*read_vbnv)(uint8_t *vbnv_copy);
void (*save_vbnv)(const uint8_t *vbnv_copy); void (*save_vbnv)(const uint8_t *vbnv_copy);
@ -43,6 +56,7 @@ struct vboot_context {
size_t *recv_len); size_t *recv_len);
void (*log_msg)(const char *fmt, va_list args); void (*log_msg)(const char *fmt, va_list args);
void (*fatal_error)(void); void (*fatal_error)(void);
void *(*get_region)(uintptr_t offset_addr, size_t size, void *dest);
}; };
#endif /* VBOOT_CONTEXT_H */ #endif /* VBOOT_CONTEXT_H */

View File

@ -18,8 +18,10 @@
*/ */
#include <stddef.h> #include <stddef.h>
#include <string.h>
#include "chromeos.h" #include "chromeos.h"
#include <boot/coreboot_tables.h> #include <boot/coreboot_tables.h>
#include <cbfs.h>
#include <cbmem.h> #include <cbmem.h>
#include <console/console.h> #include <console/console.h>
#include <payload_loader.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); 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) static void *vboot_get_payload(size_t *len)
{ {
struct vboot_handoff *vboot_handoff; struct vboot_handoff *vboot_handoff;

View File

@ -20,6 +20,7 @@
#include <arch/stages.h> #include <arch/stages.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h> #include <stddef.h>
#include <string.h>
#include <cbfs.h> #include <cbfs.h>
#include <cbmem.h> #include <cbmem.h>
#include <console/console.h> #include <console/console.h>
@ -37,10 +38,29 @@
#include "vboot_context.h" #include "vboot_context.h"
#include "vboot_handoff.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) static void vboot_run_stub(struct vboot_context *context)
{ {
struct rmod_stage_load rmod_stage = { struct rmod_stage_load rmod_stage = {
.cbmem_id = 0xffffffff, .cbmem_id = TEMP_CBMEM_ID_VBOOT,
.name = CONFIG_CBFS_PREFIX "/vboot", .name = CONFIG_CBFS_PREFIX "/vboot",
}; };
void (*entry)(struct vboot_context *context); void (*entry)(struct vboot_context *context);
@ -73,6 +93,142 @@ static void fatal_error(void)
hard_reset(); 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) static void vboot_invoke_wrapper(struct vboot_handoff *vboot_handoff)
{ {
VbCommonParams cparams; VbCommonParams cparams;
@ -104,30 +260,30 @@ static void vboot_invoke_wrapper(struct vboot_handoff *vboot_handoff)
context.cparams = &cparams; context.cparams = &cparams;
context.fparams = &fparams; 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_blob = &vboot_handoff->shared_data[0];
cparams.shared_data_size = VB_SHARED_DATA_MIN_SIZE; cparams.shared_data_size = VB_SHARED_DATA_MIN_SIZE;
cparams.caller_context = &context; cparams.caller_context = &context;
fparams.verification_size_A = locate_region("GBB", &context.gbb);
find_fmap_entry("VBLOCK_A", &fparams.verification_block_A); locate_region("VBLOCK_A", &context.vblock_a);
fparams.verification_size_B = locate_region("VBLOCK_B", &context.vblock_b);
find_fmap_entry("VBLOCK_B", &fparams.verification_block_B); locate_region("FW_MAIN_A", &context.fw_a);
locate_region("FW_MAIN_B", &context.fw_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. */ /* Check all fmap entries. */
if (context.fw_a == NULL || context.fw_b == NULL || if (context.fw_a.size < 0 || context.fw_b.size < 0 ||
fparams.verification_block_A == NULL || context.vblock_a.size < 0 || context.vblock_b.size < 0 ||
fparams.verification_block_B == NULL || context.gbb.size < 0) {
cparams.gbb_data == NULL) {
printk(BIOS_DEBUG, "Not all fmap entries found for vboot.\n"); printk(BIOS_DEBUG, "Not all fmap entries found for vboot.\n");
return; return;
} }
/* Fill in vboot parameters. */
if (vboot_fill_params(&context)) {
vboot_clean_up(&context);
return;
}
/* Initialize callbacks. */ /* Initialize callbacks. */
context.read_vbnv = &read_vbnv; context.read_vbnv = &read_vbnv;
context.save_vbnv = &save_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.tis_sendrecv = &tis_sendrecv;
context.log_msg = &log_msg; context.log_msg = &log_msg;
context.fatal_error = &fatal_error; context.fatal_error = &fatal_error;
context.get_region = &vboot_get_region;
vboot_run_stub(&context); vboot_run_stub(&context);
fill_handoff(&context);
vboot_clean_up(&context);
} }
#if CONFIG_RELOCATABLE_RAMSTAGE #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", printk(BIOS_DEBUG, "RW ramstage image at 0x%08x, 0x%08x bytes.\n",
fwc->address, fwc->size); 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) { if (stage == NULL) {
printk(BIOS_DEBUG, "Unable to get RW ramstage region.\n"); printk(BIOS_DEBUG, "Unable to get RW ramstage region.\n");

View File

@ -31,43 +31,9 @@
/* Keep a global context pointer around for the callbacks to use. */ /* Keep a global context pointer around for the callbacks to use. */
static struct vboot_context *gcontext; 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) static void vboot_wrapper(void *arg)
{ {
int i;
VbError_t res; VbError_t res;
const struct components *components;
struct vboot_context *context; struct vboot_context *context;
context = arg; context = arg;
@ -86,23 +52,6 @@ static void vboot_wrapper(void *arg)
if (res != VBERROR_SUCCESS) if (res != VBERROR_SUCCESS)
return; 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, ...) 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); 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) VbError_t VbExHashFirmwareBody(VbCommonParams *cparams, uint32_t firmware_index)
{ {
uint8_t *data; uint8_t *data;
uint32_t size; struct vboot_region *region;
uint32_t data_size; struct vboot_context *ctx;
struct components *components; size_t data_size;
uint32_t i; uintptr_t offset_addr;
ctx = cparams->caller_context;
switch (firmware_index) { switch (firmware_index) {
case VB_SELECT_FIRMWARE_A: case VB_SELECT_FIRMWARE_A:
data = gcontext->fw_a; region = &ctx->fw_a;
size = gcontext->fw_a_size;
break; break;
case VB_SELECT_FIRMWARE_B: case VB_SELECT_FIRMWARE_B:
data = gcontext->fw_b; region = &ctx->fw_b;
size = gcontext->fw_b_size;
break; break;
default: default:
return VBERROR_UNKNOWN; return VBERROR_UNKNOWN;
} }
components = (void *)data; data_size = region->size;
data_size = sizeof(struct components); offset_addr = region->offset_addr;
while (data_size) {
size_t block_size;
if (components->num_components > MAX_NUM_COMPONENTS) block_size = get_hash_block_size(data_size);
data = ctx->get_region(offset_addr, block_size, NULL);
if (data == NULL)
return VBERROR_UNKNOWN; return VBERROR_UNKNOWN;
VbUpdateFirmwareBodyHash(cparams, data, block_size);
data_size += data_size -= block_size;
components->num_components * sizeof(struct component_entry); offset_addr += block_size;
}
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; return VBERROR_SUCCESS;
} }
@ -274,4 +231,27 @@ VbError_t VbExTpmSendReceive(const uint8_t *request, uint32_t request_length,
return VBERROR_SUCCESS; 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); RMODULE_ENTRY(vboot_wrapper);