diff options
Diffstat (limited to 'src/vboot/common.c')
-rw-r--r-- | src/vboot/common.c | 182 |
1 files changed, 182 insertions, 0 deletions
diff --git a/src/vboot/common.c b/src/vboot/common.c new file mode 100644 index 0000000000..3fa96577fd --- /dev/null +++ b/src/vboot/common.c @@ -0,0 +1,182 @@ +/* + * This file is part of the coreboot project. + * + * Copyright (C) 2014 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. + */ + +#include <assert.h> +#include <cbfs.h> +#include <cbmem.h> +#include <console/console.h> +#include <reset.h> +#include <string.h> +#include <vb2_api.h> +#include <vboot/misc.h> +#include <vboot/symbols.h> +#include <vboot/vboot_common.h> + +struct selected_region { + uint32_t offset; + uint32_t size; +}; + +/* + * 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. Keep the struct cpu architecture agnostic as it crosses + * stage boundaries. + */ +struct vb2_working_data { + struct selected_region selected_region; + /* offset of the buffer from the start of this struct */ + uint32_t buffer_offset; + uint32_t buffer_size; +}; + +static const size_t vb_work_buf_size = 16 * KiB; + +static struct vb2_working_data * const vboot_get_working_data(void) +{ + if (IS_ENABLED(CONFIG_VBOOT_DYNAMIC_WORK_BUFFER)) + /* cbmem_add() does a cbmem_find() first. */ + return cbmem_add(CBMEM_ID_VBOOT_WORKBUF, vb_work_buf_size); + else + return (struct vb2_working_data *)_vboot2_work; +} + +static size_t vb2_working_data_size(void) +{ + if (IS_ENABLED(CONFIG_VBOOT_DYNAMIC_WORK_BUFFER)) + return vb_work_buf_size; + else + return _vboot2_work_size; +} + +static struct selected_region *vb2_selected_region(void) +{ + struct selected_region *sel_reg = NULL; + + /* Ramstage and postcar always uses cbmem as a source of truth. */ + if (ENV_RAMSTAGE || ENV_POSTCAR) + sel_reg = cbmem_find(CBMEM_ID_VBOOT_SEL_REG); + else if (ENV_ROMSTAGE) { + /* Try cbmem first. Fall back on working data if not found. */ + sel_reg = cbmem_find(CBMEM_ID_VBOOT_SEL_REG); + + if (sel_reg == NULL) { + struct vb2_working_data *wd = vboot_get_working_data(); + sel_reg = &wd->selected_region; + } + } else { + /* Stages such as bootblock and verstage use working data. */ + struct vb2_working_data *wd = vboot_get_working_data(); + sel_reg = &wd->selected_region; + } + + return sel_reg; +} + +void vb2_init_work_context(struct vb2_context *ctx) +{ + struct vb2_working_data *wd; + size_t work_size; + + /* First initialize the working data region. */ + work_size = vb2_working_data_size(); + wd = vboot_get_working_data(); + memset(wd, 0, work_size); + + /* + * vboot prefers 16-byte alignment. This takes away 16 bytes + * from the VBOOT2_WORK region, but the vboot devs said that's okay. + */ + wd->buffer_offset = ALIGN_UP(sizeof(*wd), 16); + wd->buffer_size = work_size - wd->buffer_offset; + + /* Initialize the vb2_context. */ + memset(ctx, 0, sizeof(*ctx)); + ctx->workbuf = (void *)vb2_get_shared_data(); + ctx->workbuf_size = wd->buffer_size; + +} + +struct vb2_shared_data *vb2_get_shared_data(void) +{ + struct vb2_working_data *wd = vboot_get_working_data(); + return (void *)((uintptr_t)wd + wd->buffer_offset); +} + +int vb2_get_selected_region(struct region *region) +{ + const struct selected_region *reg = vb2_selected_region(); + + if (reg == NULL) + return -1; + + if (reg->offset == 0 && reg->size == 0) + return -1; + + region->offset = reg->offset; + region->size = reg->size; + + return 0; +} + +void vb2_set_selected_region(const struct region *region) +{ + struct selected_region *reg = vb2_selected_region(); + + assert(reg != NULL); + + reg->offset = region_offset(region); + reg->size = region_sz(region); +} + +int vb2_is_slot_selected(void) +{ + const struct selected_region *reg = vb2_selected_region(); + + assert(reg != NULL); + + return reg->size > 0; +} + +void vb2_store_selected_region(void) +{ + const struct vb2_working_data *wd; + struct selected_region *sel_reg; + + /* Always use the working data in this path since it's the object + * which has the result.. */ + wd = vboot_get_working_data(); + + sel_reg = cbmem_add(CBMEM_ID_VBOOT_SEL_REG, sizeof(*sel_reg)); + + assert(sel_reg != NULL); + + sel_reg->offset = wd->selected_region.offset; + sel_reg->size = wd->selected_region.size; +} + +/* + * For platforms that employ VBOOT_DYNAMIC_WORK_BUFFER, the vboot + * verification doesn't happen until after cbmem is brought online. + * Therefore, the selected region contents would not be initialized + * so don't automatically add results when cbmem comes online. + */ +#if !IS_ENABLED(CONFIG_VBOOT_DYNAMIC_WORK_BUFFER) +static void vb2_store_selected_region_cbmem(int unused) +{ + vb2_store_selected_region(); +} +ROMSTAGE_CBMEM_INIT_HOOK(vb2_store_selected_region_cbmem) +#endif |