/* * This file is part of the coreboot project. * * Copyright 2015 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. */ #include #include #include #include #include #include #include #include "misc.h" #include "../vboot_handoff.h" #include "../symbols.h" /* The stage loading code is compiled and entered from multiple stages. The * helper functions below attempt to provide more clarity on when certain * code should be called. */ static int verification_should_run(void) { if (ENV_VERSTAGE && IS_ENABLED(CONFIG_SEPARATE_VERSTAGE)) return 1; if (!IS_ENABLED(CONFIG_SEPARATE_VERSTAGE)) { if (ENV_ROMSTAGE && IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE)) return 1; if (ENV_BOOTBLOCK && IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK)) return 1; } return 0; } static int verstage_should_load(void) { if (!IS_ENABLED(CONFIG_SEPARATE_VERSTAGE)) return 0; if (ENV_ROMSTAGE && IS_ENABLED(CONFIG_VBOOT_STARTS_IN_ROMSTAGE)) return 1; if (ENV_BOOTBLOCK && IS_ENABLED(CONFIG_VBOOT_STARTS_IN_BOOTBLOCK)) return 1; return 0; } static void init_vb2_working_data(void) { struct vb2_working_data *wd; size_t work_size; 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; } static int vboot_loader_active(struct prog *prog) { struct vb2_working_data *wd; int run_verification; run_verification = verification_should_run(); if (run_verification) { init_vb2_working_data(); verstage_main(); } else if (verstage_should_load()) { struct prog verstage = { .type = PROG_VERSTAGE, .name = CONFIG_CBFS_PREFIX "/verstage", }; printk(BIOS_DEBUG, "VBOOT: Loading verstage.\n"); /* load verstage from RO */ if (cbfs_boot_locate(&verstage.rdev, verstage.name, NULL) || cbfs_prog_stage_load(&verstage)) die("failed to load verstage"); /* verify and select a slot */ prog_run(&verstage); /* This is not actually possible to hit this condition at * runtime, but this provides a hint to the compiler for dead * code elimination below. */ if (!IS_ENABLED(CONFIG_RETURN_FROM_VERSTAGE)) return 0; } /* Fill in vboot handoff structure before moving to ramstage so all * downstream users have access to vboot results. */ if (ENV_ROMSTAGE) vboot_fill_handoff(); wd = vboot_get_working_data(); if (vboot_is_slot_selected(wd)) return 1; return 0; } static int vboot_locate_by_components(const struct region_device *fw_main, struct prog *prog) { struct vboot_components *fw_info; size_t metadata_sz; size_t offset; size_t size; struct region_device *fw = &prog->rdev; int fw_index = 0; if (prog->type == PROG_ROMSTAGE) fw_index = CONFIG_VBOOT_ROMSTAGE_INDEX; else if (prog->type == PROG_RAMSTAGE) fw_index = CONFIG_VBOOT_RAMSTAGE_INDEX; else if (prog->type == PROG_PAYLOAD) fw_index = CONFIG_VBOOT_BOOT_LOADER_INDEX; else if (prog->type == PROG_REFCODE) fw_index = CONFIG_VBOOT_REFCODE_INDEX; else if (prog->type == PROG_BL31) fw_index = CONFIG_VBOOT_BL31_INDEX; else die("Invalid program type for vboot."); metadata_sz = sizeof(*fw_info); metadata_sz += MAX_PARSED_FW_COMPONENTS * sizeof(fw_info->entries[0]); fw_info = rdev_mmap(fw_main, 0, metadata_sz); if (fw_info == NULL) { printk(BIOS_INFO, "No component metadata.\n"); return -1; } if (fw_index >= fw_info->num_components) { printk(BIOS_INFO, "invalid index: %d\n", fw_index); rdev_munmap(fw_main, fw_info); return -1; } offset = fw_info->entries[fw_index].offset; size = fw_info->entries[fw_index].size; rdev_munmap(fw_main, fw_info); if (rdev_chain(fw, fw_main, offset, size)) { printk(BIOS_INFO, "invalid offset or size\n"); return -1; } return 0; } static int vboot_locate_by_multi_cbfs(const struct region_device *fw_main, struct prog *prog) { struct cbfsd cbfs; struct region_device rdev; struct cbfs_props props; if (cbfs_boot_region_properties(&props)) return -1; if (rdev_chain(&rdev, fw_main, props.offset, props.size)) return -1; cbfs.rdev = &rdev; cbfs.align = props.align; return cbfs_locate(&prog->rdev, &cbfs, prog->name, NULL); } static int vboot_prog_locate(const struct region_device *fw_main, struct prog *prog) { if (IS_ENABLED(CONFIG_MULTIPLE_CBFS_INSTANCES)) return vboot_locate_by_multi_cbfs(fw_main, prog); else return vboot_locate_by_components(fw_main, prog); } /* This function is only called when vboot_loader_active() returns 1. That * means we are taking vboot paths. */ static int vboot_locate(struct prog *prog) { struct vb2_working_data *wd; struct region_device fw_main; /* Code size optimization. We'd never actually get called under the * followin cirumstances because verstage was loaded and ran -- never * returning. */ if (verstage_should_load() && !IS_ENABLED(CONFIG_RETURN_FROM_VERSTAGE)) return 0; wd = vboot_get_working_data(); if (vb2_get_selected_region(wd, &fw_main)) die("failed to reference selected region\n"); return vboot_prog_locate(&fw_main, prog); } const struct prog_loader_ops vboot_loader = { .name = "VBOOT", .is_loader_active = vboot_loader_active, .locate = vboot_locate, };