blob: 93e9ef57f969922fe152b393a21d44643d77a9c3 [file] [log] [blame]
/*
* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*
* Alternatively, this software may be distributed under the terms of the
* GNU General Public License ("GPL") version 2 as published by the Free
* Software Foundation.
*/
#include <common.h>
#include <cros_ec.h>
#include <errno.h>
#include <hash.h>
#include <malloc.h>
#include <mapmem.h>
#include <os.h>
#include <u-boot/sha256.h>
#include <spi_flash.h>
#include <spl.h>
#include <tis.h>
#include <asm/io.h>
#include <linux/lzo.h>
#include <cros/common.h>
#include <cros/cros_init.h>
#include <cros/hasher_state.h>
#include <cros/memory_wipe.h>
#include <cros/nvstorage.h>
#include <cros/vboot.h>
#include <cros/vboot_flag.h>
#ifdef CONFIG_EXYNOS5
#include <asm/arch/spl.h>
#endif
#ifdef CONFIG_X86
#include <asm/arch/sysinfo.h>
#endif
DECLARE_GLOBAL_DATA_PTR;
__weak const char *cros_fdt_get_mem_type(void)
{
return NULL;
}
int vboot_ro_init(struct vboot_info *vboot)
{
const void *blob = gd->fdt_blob;
GoogleBinaryBlockHeader gbb;
memset(vboot, '\0', sizeof(*vboot));
vboot->valid = true;
bootstage_mark_name(BOOTSTAGE_ID_ALLOC, "vboot_readonly");
VBDEBUG("Starting read-only firmware\n");
if (cros_init())
goto err;
#ifdef CONFIG_CROS_EC_SANDBOX
struct cros_ec_dev *cros_ec = board_get_cros_ec_dev();
/*
* Check for EC keyboard, since on sandbox we want to support recovery
* mode, but the EC needs a chance to scan keys before it will know
* whether recovery mode is requested.
*
* This needs to be done after the LCD is inited (since until then
* the SDL keyboard will not work), and is Chrome OS-specific, so we
* may as well do it here.
*/
cros_ec_check_keyboard(cros_ec);
#endif
if (vboot_load_config(blob, vboot)) {
VBDEBUG("failed to read /chromeos-config\n");
goto err;
}
vboot_persist_dump("ro", vboot);
if (vboot_flag_fetch(VBOOT_FLAG_WRITE_PROTECT, &vboot->wpsw) ||
vboot_flag_fetch(VBOOT_FLAG_DEVELOPER, &vboot->devsw) ||
vboot_flag_fetch(VBOOT_FLAG_OPROM_LOADED, &vboot->oprom)) {
VBDEBUG("failed to fetch gpio\n");
goto err;
}
vboot_flag_dump(VBOOT_FLAG_WRITE_PROTECT, &vboot->wpsw);
vboot_flag_dump(VBOOT_FLAG_DEVELOPER, &vboot->devsw);
vboot_flag_dump(VBOOT_FLAG_OPROM_LOADED, &vboot->oprom);
if (cros_fdtdec_config_has_prop(blob, "oprom-matters")) {
VBDEBUG("FDT says oprom-matters\n");
vboot->oprom_matters = 1;
}
if (cros_fdtdec_flashmap(blob, &vboot->fmap)) {
VBDEBUG("failed to decode fmap\n");
goto err;
}
dump_fmap(&vboot->fmap);
/*
* With EFS we need a recovery image since the RO one does not have
* display support, USB, etc.
*/
if (vboot->use_efs && !vboot->fmap.readonly.boot_rec.length) {
VBDEBUG("EFS requires a recovery image\n");
goto err;
}
if (firmware_storage_open(&vboot->file)) {
VBDEBUG("failed to open firmware storage\n");
goto err;
}
/* Read read-only firmware ID */
if (vboot->file.read(&vboot->file,
vboot->fmap.readonly.firmware_id.offset,
min((uint)sizeof(vboot->readonly_firmware_id),
vboot->fmap.readonly.firmware_id.length),
vboot->readonly_firmware_id)) {
VBDEBUG("failed to read firmware ID\n");
goto err;
}
VBDEBUG("read-only firmware id: \"%s\"\n", vboot->readonly_firmware_id);
/*
* Read hardware ID
* TODO(sjg@chromium.org): It's really ugly having vboot read from the
* GBB structure which is owned by vboot. We should considering having
* a vboot API to read this.
*/
if (vboot->file.read(&vboot->file, vboot->fmap.readonly.gbb.offset,
sizeof(gbb), &gbb)) {
VBDEBUG("failed to read GBB header\n");
goto err;
}
if (vboot->file.read(&vboot->file,
vboot->fmap.readonly.gbb.offset + gbb.hwid_offset,
min((uint)sizeof(vboot->hardware_id),
gbb.hwid_size),
&vboot->hardware_id)) {
VBDEBUG("failed to read hardware ID\n");
goto err;
}
VBDEBUG("hardware id: \"%s\"\n", vboot->hardware_id);
vboot->nvcontext_method = nvstorage_get_method();
vboot->nvcontext_lba = CHROMEOS_VBNVCONTEXT_LBA;
vboot->nvcontext_offset = 0;
vboot->nvcontext_size = VBNV_BLOCK_SIZE;
vboot->active_ec_firmware = ACTIVE_EC_FIRMWARE_RO;
vboot->ddr_type = cros_fdt_get_mem_type();
return 0;
err:
return vboot_set_error(vboot, __func__, VBERROR_UNKNOWN);
}
int vboot_ro_vbinit(struct vboot_info *vboot)
{
VbInitParams *iparams = &vboot->iparams;
#ifdef CONFIG_CROS_EC
struct cros_ec_dev *cros_ec = board_get_cros_ec_dev();
VbError_t err;
if (cros_ec) {
uint32_t ec_events = 0;
const uint32_t kb_rec_mask =
EC_HOST_EVENT_MASK(EC_HOST_EVENT_KEYBOARD_RECOVERY);
/* Read keyboard recovery flag from EC, then clear it */
if (cros_ec_get_host_events(cros_ec, &ec_events)) {
/*
* TODO: what can we do if that fails? Request
* recovery? We don't simply want to fail, because
* that'll prevent us from going into recovery mode.
* We don't want to go into recovery mode
* automatically, because that'll break snow.
*/
VBDEBUG("VbInit: unable to read EC events\n");
ec_events = 0;
}
if (ec_events & kb_rec_mask) {
iparams->flags |= VB_INIT_FLAG_REC_BUTTON_PRESSED;
if (cros_ec_clear_host_events(cros_ec, kb_rec_mask))
VBDEBUG("VbInit: unable to clear EC KB recovery event\n");
}
}
#endif
/*
* We can't support RO normal with Early Firmware Selection - the RW
* U-Boot must always run since the RO U-Boot is not capable of
* running recovery mode (no display, etc.)
*/
if (!vboot->use_efs &&
cros_fdtdec_config_has_prop(gd->fdt_blob, "twostop-optional"))
iparams->flags |= VB_INIT_FLAG_RO_NORMAL_SUPPORT;
VBDEBUG("RO Normal %sabled\n",
iparams->flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT ? "en" : "dis");
if (vboot->wpsw.value)
iparams->flags |= VB_INIT_FLAG_WP_ENABLED;
if (vboot->recsw.value)
iparams->flags |= VB_INIT_FLAG_REC_BUTTON_PRESSED;
if (vboot->devsw.value)
iparams->flags |= VB_INIT_FLAG_DEV_SWITCH_ON;
if (vboot->oprom.value)
iparams->flags |= VB_INIT_FLAG_OPROM_LOADED;
if (vboot->oprom_matters)
iparams->flags |= VB_INIT_FLAG_OPROM_MATTERS;
if (cros_fdtdec_config_has_prop(gd->fdt_blob, "virtual-dev-switch"))
iparams->flags |= VB_INIT_FLAG_VIRTUAL_DEV_SWITCH;
if (cros_fdtdec_config_has_prop(gd->fdt_blob, "ec-software-sync"))
iparams->flags |= VB_INIT_FLAG_EC_SOFTWARE_SYNC;
if (cros_fdtdec_config_has_prop(gd->fdt_blob, "ec-slow-update"))
iparams->flags |= VB_INIT_FLAG_EC_SLOW_UPDATE;
if (vboot->file.sw_wp_enabled(&vboot->file))
iparams->flags |= VB_INIT_FLAG_SW_WP_ENABLED;
VBDEBUG("iparams->flags: %08x\n", iparams->flags);
vboot_init_cparams(vboot, &vboot->cparams);
err = VbInit(&vboot->cparams, &vboot->iparams);
if (err) {
VBDEBUG("VbInit: %#x\n", err);
return vboot_set_error(vboot, __func__, err);
}
return 0;
}
#if defined(CONFIG_SYS_COREBOOT)
static void setup_arch_unused_memory(struct vboot_info *vboot,
struct memory_wipe *wipe)
{
int i;
/* Add ranges that describe RAM. */
for (i = 0; i < lib_sysinfo.n_memranges; i++) {
struct memrange *range = &lib_sysinfo.memrange[i];
if (range->type == CB_MEM_RAM) {
memory_wipe_add(wipe, range->base,
range->base + range->size);
}
}
/*
* Remove ranges that don't. These should take precedence, so they're
* done last and in their own loop.
*/
for (i = 0; i < lib_sysinfo.n_memranges; i++) {
struct memrange *range = &lib_sysinfo.memrange[i];
if (range->type != CB_MEM_RAM) {
memory_wipe_sub(wipe, range->base,
range->base + range->size);
}
}
}
#else
static void setup_arch_unused_memory(struct vboot_info *vboot,
struct memory_wipe *wipe)
{
struct fdt_memory ramoops, lp0;
int bank;
for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) {
if (!gd->bd->bi_dram[bank].size)
continue;
memory_wipe_add(wipe, gd->bd->bi_dram[bank].start,
gd->bd->bi_dram[bank].start +
gd->bd->bi_dram[bank].size);
}
/* Excludes kcrashmem if in FDT */
if (cros_fdtdec_memory(gd->fdt_blob, "/ramoops", &ramoops))
VBDEBUG("RAMOOPS not contained within FDT\n");
else
memory_wipe_sub(wipe, ramoops.start, ramoops.end);
/* Excludes the LP0 vector; only applicable to tegra platforms */
if (cros_fdtdec_memory(gd->fdt_blob, "/lp0", &lp0))
VBDEBUG("LP0 not contained within FDT\n");
else
memory_wipe_sub(wipe, lp0.start, lp0.end);
}
#endif
static uintptr_t get_current_sp(void)
{
#ifdef CONFIG_SANDBOX
return gd->start_addr_sp;
#else
uintptr_t addr;
addr = (uintptr_t)&addr;
return addr;
#endif
}
static void wipe_unused_memory(struct vboot_info *vboot)
{
struct memory_wipe wipe;
memory_wipe_init(&wipe);
if (vboot->use_efs) {
/* TODO: Clear SDRAM */
} else {
setup_arch_unused_memory(vboot, &wipe);
/* Exclude relocated u-boot structures. */
memory_wipe_sub(&wipe,
get_current_sp() - MEMORY_WIPE_STACK_MARGIN,
gd->ram_top);
}
memory_wipe_execute(&wipe);
}
int vboot_ro_flags(struct vboot_info *vboot)
{
uint32_t out_flags = vboot->iparams.out_flags;
if (cros_fdtdec_config_has_prop(gd->fdt_blob, "virtual-dev-switch")) {
vboot->devsw.value =
(out_flags & VB_INIT_OUT_ENABLE_DEVELOPER) ? 1 : 0;
VBDEBUG("Developer switch = %d\n", vboot->devsw.value);
}
if ((out_flags & VB_INIT_OUT_CLEAR_RAM) &&
!cros_fdtdec_config_has_prop(gd->fdt_blob, "disable-memory-clear"))
wipe_unused_memory(vboot);
return 0;
}
/* This can only be called after key block has been verified */
static size_t firmware_body_size(const VbKeyBlockHeader *keyblock)
{
const VbFirmwarePreambleHeader const *preamble;
preamble = (VbFirmwarePreambleHeader *)
((void *)keyblock + keyblock->key_block_size);
return preamble->body_signature.data_size;
}
static int vboot_alloc_read(struct vboot_info *vboot, void **ptrp,
uint32_t offset, int size, const char *name)
{
*ptrp = cros_memalign_cache(size);
if (!*ptrp) {
VBDEBUG("failed to allocate %s\n", name);
return -ENOMEM;
}
if (vboot->file.read(&vboot->file, offset, size, *ptrp)) {
VBDEBUG("fail to read %s\n", name);
return -EIO;
}
return 0;
}
/* When memory is limited, use the piecemeal approach */
VbError_t vboot_hash_firmware(struct vboot_info *vboot,
unsigned int fw_size,
struct fmap_entry *entry)
{
const int buf_size = 8192;
int offset, todo;
uint8_t *buf;
buf = malloc(buf_size);
if (!buf) {
VBDEBUG("Cannot allocate firmware buffer\n");
return 1;
}
for (offset = 0; offset < fw_size; offset += todo) {
todo = min(fw_size - offset, (uint)buf_size);
if (vboot->file.read(&vboot->file, entry->offset + offset,
todo, buf)) {
VBDEBUG("fail to read firmware offset %#x\n",
entry->offset + offset);
free(buf);
return 1;
}
VbUpdateFirmwareBodyHash(&vboot->cparams, buf, todo);
}
free(buf);
return 0;
}
static int vboot_verify_firmware(struct vboot_info *vboot,
struct vboot_fw_info *fw)
{
#if defined(CONFIG_EXYNOS5)
struct spl_hash *hash = NULL;
struct fmap_entry *entry;
uint8_t digest[SHA256_SUM_LEN];
fdt_addr_t addr;
fdt_size_t size;
void *buf;
int len;
int ret;
hash = spl_extract_hash(fw->cache);
if (!hash)
return 0;
/*
* TODO(sjg@chromium.org): Need to read firmware in chunks. For now
* use SDRAM.
*/
ret = fdtdec_decode_memory_region(gd->fdt_blob, -1, "u-boot", NULL,
&addr, &size);
if (ret) {
VBDEBUG("failed to find u-boot memory region: %d\n", ret);
return -1;
}
buf = map_sysmem(addr, size);
entry = &fw->fw_entry->boot;
if (vboot->file.read(&vboot->file, entry->offset, entry->used,
buf)) {
VBDEBUG("fail to read firmware offset %#x\n", entry->offset);
return -1;
}
len = SHA256_SUM_LEN;
if (hash_block("sha256", buf, entry->used, digest, &len)) {
VBDEBUG("hash_block failed\n");
return -1;
}
if (memcmp(hash->digest, digest, len)) {
VBDEBUG("Early firmware verify failure\n");
return -1;
}
VBDEBUG("Early firmware verify completed\n");
#endif
return 0;
}
VbError_t VbExHashFirmwareBody(VbCommonParams *cparams, uint32_t index)
{
struct vboot_info *vboot;
struct vboot_fw_info *fw;
if (vboot_is_legacy())
return load_firmware_VbExHashFirmwareBody(cparams, index);
vboot = cparams->caller_context;
if (index != VB_SELECT_FIRMWARE_A && index != VB_SELECT_FIRMWARE_B) {
VBDEBUG("incorrect firmware index: %d\n", index);
return -1;
}
if (vboot->persist && (vboot->persist->flags & (1 << index))) {
VBDEBUG("firmware index: %d has been tried - skipping\n",
index);
return -1;
}
/*
* The key block has been verified. It is safe now to infer the actual
* firmware body size from the key block.
*/
fw = &vboot->fw[index];
fw->size = firmware_body_size(fw->vblock);
VBDEBUG("Firmware size is %#0x\n", fw->size);
if (vboot_alloc_read(vboot, &fw->cache, fw->entry->offset, fw->size,
"sel fw") == -ENOMEM) {
VBDEBUG("Limited memory - using streaming verify\n");
if (vboot_hash_firmware(vboot, fw->size, fw->entry))
return -1;
} else {
VbUpdateFirmwareBodyHash(cparams, fw->cache, fw->size);
}
/*
* If we are only reading the SPL, then we can verify the U-Boot that
* it will load either now, while there is time to report a problem
* to vboot, or later (when running the SPL) at which point we will
* need to reboot and try the other firmware
*/
if (vboot->early_firmware_verification &&
vboot_verify_firmware(vboot, fw)) {
VBDEBUG("RW U-Boot failed to verify\n");
return -1;
}
return 0;
}
int vboot_ro_select_firmware(struct vboot_info *vboot)
{
VbSelectFirmwareParams *fparams = &vboot->fparams;
struct twostop_fmap *fmap = &vboot->fmap;
struct fmap_entry *entry;
VbError_t err = VBERROR_UNKNOWN;
struct vboot_fw_info *fw;
int selection = -1;
uint32_t vlength;
int len;
int i;
vlength = fmap->readwrite_a.vblock.length;
assert(vlength == fmap->readwrite_b.vblock.length);
/* crbug.com/205554 */
vlength = 8192;
/* Sort out all our regions */
fw = &vboot->fw[VB_SELECT_FIRMWARE_A];
fw->fw_entry = &fmap->readwrite_a;
if (vboot_alloc_read(vboot, &fw->vblock, fw->fw_entry->vblock.offset,
vlength, "vblock a"))
goto out;
fparams->verification_block_A = fw->vblock;
fw->entry = vboot->use_efs ? &fw->fw_entry->spl : &fw->fw_entry->boot;
fw = &vboot->fw[VB_SELECT_FIRMWARE_B];
fw->fw_entry = &fmap->readwrite_b;
if (vboot_alloc_read(vboot, &fw->vblock, fw->fw_entry->vblock.offset,
vlength, "vblock b"))
goto out;
fparams->verification_block_B = fw->vblock;
fw->entry = vboot->use_efs ? &fw->fw_entry->spl : &fw->fw_entry->boot;
/*
* Recovery uses the recovery image, unless there is none available,
* in which case the RO image is used.
*/
fw = &vboot->fw[VB_SELECT_FIRMWARE_RECOVERY];
fw->fw_entry = &fmap->readonly;
fw->entry = vboot->use_efs
? &fw->fw_entry->spl_rec
: &fw->fw_entry->boot_rec;
if (!fw->entry->length) {
fw->fw_entry = &fmap->readonly;
fw->entry = NULL;
}
/* For RO normal, we allow the RO image to run all stages */
fw = &vboot->fw[VB_SELECT_FIRMWARE_READONLY];
if (vboot->iparams.flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT)
fw->fw_entry = &fmap->readonly;
/* Sanity check firmwares */
for (i = VB_SELECT_FIRMWARE_A; i <= VB_SELECT_FIRMWARE_B; i++) {
if (!(vboot->iparams.flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT) &&
i == VB_SELECT_FIRMWARE_READONLY)
continue;
fw = &vboot->fw[i];
if (!fw->fw_entry || !fw->entry) {
VBDEBUG("Firmware entry %d missing\n", i);
goto out;
}
if (!fw->entry->length) {
VBDEBUG("Firmware entry %d, offset = %#x, size = %#x\n",
i, fw->entry->offset, fw->entry->length);
goto out;
}
}
fparams->verification_size_A = vlength;
fparams->verification_size_B = vlength;
err = VbSelectFirmware(&vboot->cparams, fparams);
if (err)
goto out;
err = VBERROR_UNKNOWN;
selection = fparams->selected_firmware;
vboot->selected_firmware = selection;
VBDEBUG("selected_firmware: %x\n", selection);
bootstage_mark_name(BOOTSTAGE_ID_ALLOC, "rw_firmware_loaded");
fw = &vboot->fw[selection];
if (selection >= VB_SELECT_FIRMWARE_COUNT || !fw->fw_entry) {
VBDEBUG("impossible selection value: %d\n", selection);
goto out;
}
/* If the recovery U-Boot is separate, we must load it */
if (selection == VB_SELECT_FIRMWARE_RECOVERY && fw->entry) {
VBDEBUG("loading recovery image size %x\n", fw->entry->used);
if (vboot_alloc_read(vboot, &fw->cache, fw->entry->offset,
fw->entry->used, "rec fw")) {
VBDEBUG("could not load recovery image %d\n",
selection);
goto out;
}
}
entry = &fw->fw_entry->firmware_id;
len = min((uint)sizeof(vboot->firmware_id), entry->length);
if (vboot->file.read(&vboot->file, entry->offset,
len, vboot->firmware_id)) {
VBDEBUG("failed to read active firmware id\n");
vboot->firmware_id[0] = '\0';
}
if (selection == VB_SELECT_FIRMWARE_RECOVERY)
vboot->firmware_type = "recovery";
else if (vboot->devsw.value)
vboot->firmware_type = "developer";
else
vboot->firmware_type = "normal";
VBDEBUG("active main firmware type : %s\n", vboot->firmware_type);
VBDEBUG("active main firmware id : \"%s\"\n", vboot->firmware_id);
/*
* fw->size is only set if the firmware was hashed. For recovery and
* RO-normal this does not happen.
*/
if (!fw->size && fw->entry)
fw->size = fw->entry->used;
/* Everything looks fine */
err = VBERROR_SUCCESS;
out:
for (i = 0; i < VB_SELECT_FIRMWARE_COUNT; i++) {
struct vboot_fw_info *fw = &vboot->fw[i];
free(fw->vblock);
if (i != selection)
free(fw->cache);
}
if (err) {
vboot_set_error(vboot, __func__, err);
return -1;
}
return 0;
}
/**
* Stash the RW SPL in IRAM so we can quickly jump to it on resume
*/
static int vboot_stash_rw_spl(struct vboot_info *vboot,
struct vboot_fw_info *fw)
{
struct vboot_spl_hdr *hdr;
fdt_addr_t base;
fdt_size_t size;
if (cros_fdtdec_decode_region(gd->fdt_blob, "rw-spl", ",efs",
&base, &size)) {
VBDEBUG("Cannot find RW SPL stash\n");
return -ENOENT;
}
hdr = map_sysmem(base, size);
hdr->signature = 0; /* Not valid yet */
if (fw->size + sizeof(*hdr) > size) {
VBDEBUG("Stash is too small for RW SPL\n");
return -ENOSPC;
}
hdr->size = fw->size;
hdr->crc32 = crc32(VBOOT_SPL_SIGNATURE, vboot->fw_dest, fw->size);
hdr->spare = 0;
memcpy(hdr + 1, vboot->fw_dest, fw->size);
/* All OK, so write the signature */
hdr->signature = VBOOT_SPL_SIGNATURE;
VBDEBUG("Stashed RW SPL for resume path at %lx, size %lx, free %lx\n",
(ulong)base, (ulong)size,
size - (ulong)fw->size - sizeof(*hdr));
/*
* This can be used as a test of SPL self-relocation. It adjusts it
* so that we boot the RW SPL in the stashed location. It should
* relocate itself and run normally.
*
* vboot->fw_dest = hdr + 1;
*/
return 0;
}
int vboot_ro_prepare(struct vboot_info *vboot)
{
enum VbSelectFirmware_t selected_firmware = vboot->selected_firmware;
struct vboot_fw_info *fw;
void *cdata_fdt;
fdt_addr_t fw_addr, base;
fdt_size_t size;
int ret;
fw = &vboot->fw[selected_firmware];
fw->uncomp_size = fw->size;
ret = fdtdec_decode_memory_region(gd->fdt_blob, -1, "u-boot",
vboot->use_efs ? ",spl" : NULL,
&fw_addr, &size);
if (ret) {
VBDEBUG("failed to find u-boot memory region: %d\n", ret);
goto err;
}
vboot->fw_dest = map_sysmem(fw_addr, fw->size);
if (!fw->fw_entry) {
VBDEBUG("firmware has not been loaded\n");
goto err;
}
VBDEBUG("jump to firmware %d at %#08x, pos %#08x, size %#x\n",
selected_firmware, (unsigned)fw_addr,
(unsigned)map_to_sysmem(fw->cache), fw->size);
/*
* TODO(sjg@chromium.org): This version of U-Boot must be loaded at a
* fixed location. It could be problematic if newer version U-Boot
* changed this address. It should be easy enough to make U-Boot
* position-independent.
*
* Note: fw->entry will be NULL if no RW firmware was loaded, which
* happens in RO-normal.
*/
if (fw->entry) {
switch (fw->entry->compress_algo) {
#ifdef CONFIG_LZO
case FMAP_COMPRESS_LZO: {
size_t unc_len;
int ret;
bootstage_start(BOOTSTAGE_ID_ACCUM_DECOMP,
"decompress_image");
ret = lzop_decompress(fw->cache, fw->size,
vboot->fw_dest, &unc_len);
if (ret < 0) {
VBDEBUG("LZO: uncompress or overwrite error %d - must RESET board to recover\n",
ret);
goto err;
}
fw->uncomp_size = unc_len;
bootstage_accum(BOOTSTAGE_ID_ACCUM_DECOMP);
break;
}
#endif
case FMAP_COMPRESS_NONE:
/* Take this out for testing */
memmove(vboot->fw_dest, fw->cache, fw->size);
break;
default:
VBDEBUG("Unsupported compression type %d\n",
fw->entry->compress_algo);
return -1;
}
}
/* Stash the RW SPL away if needed */
if (vboot->use_efs) {
if (vboot_stash_rw_spl(vboot, fw))
return -1;
}
#ifdef CONFIG_SPL_HEADER_SIZE
/* Skip the header when we jump to the SPL */
if (vboot->use_efs)
vboot->fw_dest += CONFIG_SPL_HEADER_SIZE;
#endif
if (cros_fdtdec_decode_region(gd->fdt_blob, "cros-system-data",
vboot->use_efs ? ",efs" : NULL,
&base, &size)) {
VBDEBUG("Cannot find cdata FDT\n");
goto err;
}
cdata_fdt = map_sysmem(base, size);
if (fdt_create_empty_tree(cdata_fdt, size)) {
VBDEBUG("Cannot create empty cdata FDT\n");
goto err;
}
if (vboot_write_to_fdt(vboot, cdata_fdt)) {
VBDEBUG("Cannot write vboot data to FDT\n");
goto err;
}
VBDEBUG("crossystem data written to FDT at %#08x, size %#08x\n",
(unsigned)map_to_sysmem(cdata_fdt), size);
return 0;
err:
return vboot_set_error(vboot, __func__, VBERROR_UNKNOWN);
}
int vboot_ro_jump(struct vboot_info *vboot)
{
struct vboot_fw_info *fw;
vboot_persist_dump("jump", vboot);
fw = &vboot->fw[vboot->selected_firmware];
bootstage_mark_name(BOOTSTAGE_ID_ALLOC, "vboot_readonly_jump");
/* RO-normal optimization */
if (vboot->iparams.flags & VB_INIT_FLAG_RO_NORMAL_SUPPORT) {
if (vboot->selected_firmware == VB_SELECT_FIRMWARE_RECOVERY ||
vboot->selected_firmware == VB_SELECT_FIRMWARE_READONLY) {
VBDEBUG("RO-normal support, skipping jump\n");
tis_close();
return 0;
}
}
VBDEBUG("Jump to rw %s at %x\n", vboot->use_efs ? "SPL" : "U-Boot",
(unsigned)map_to_sysmem(vboot->fw_dest));
cleanup_before_linux();
#ifdef CONFIG_SANDBOX
os_jump_to_image(vboot->fw_dest, fw->uncomp_size);
#else
ulong spi_offset;
if (vboot->selected_firmware == VB_SELECT_FIRMWARE_RECOVERY)
spi_offset = fw->fw_entry->boot_rec.offset;
else
spi_offset = fw->fw_entry->boot.offset;
((void(*)(ulong tag, ulong fw, ulong spi_offset))vboot->fw_dest)
(SPL_RUNNING_FROM_UBOOT,
vboot->selected_firmware,
spi_offset);
#endif
/* It is an error if readwrite firmware returns */
return -1;
}