blob: c4d651a29bd8e2cfde11315e5c83562ae87ffeb5 [file] [log] [blame]
/*
* 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 "symbols.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 __ROMSTAGE__
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 **)&region->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(*vbc);
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_media default_media, *media = &default_media;
uintptr_t fc_addr;
uint32_t fc_size;
void *entry;
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;
}
init_default_cbfs_media(media);
/* we're making cbfs access offset outside of the region managed by
* cbfs. this works because cbfs_load_stage_by_offset does not check
* the offset. */
entry = cbfs_load_stage_by_offset(media, fc_addr);
if (entry == (void *)-1)
entry = NULL;
return entry;
}
struct vb2_working_data * const vboot_get_working_data(void)
{
return (struct vb2_working_data *)_vboot2_work;
}
void vboot_reboot(void)
{
hard_reset();
die("failed to reboot");
}
#endif