blob: cad1d800b45d33e60997f8b5fc12aa3ef2887cb4 [file] [log] [blame]
/*
* Copyright (c) 2012 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 <command.h>
#include <fdtdec.h>
#include <lcd.h>
#include <malloc.h>
#include <mkbp.h>
#include <linux/compiler.h>
#include <cros/boot_kernel.h>
#include <cros/common.h>
#include <cros/crossystem_data.h>
#include <cros/cros_fdtdec.h>
#include <cros/cros_init.h>
#include <cros/firmware_storage.h>
#include <cros/gbb.h>
#include <cros/hasher_state.h>
#include <cros/memory_wipe.h>
#include <cros/nvstorage.h>
#include <cros/power_management.h>
#include <cros/vboot_flag.h>
#include <linux/lzo.h>
#include <spi.h>
#include <usb.h>
#ifdef CONFIG_SPI_FLASH
/* to read SW write-protect settings from flash chip */
#include <spi_flash.h>
#endif
#ifdef CONFIG_VIDEO_TEGRA
/* for tegra_lcd_check_next_stage() */
#include <asm/arch/display.h>
#endif
#ifdef CONFIG_EXYNOS_DISPLAYPORT
/* for exynos_lcd_check_next_stage() */
#include <asm/arch/s5p-dp.h>
#endif
#include <gbb_header.h> /* for GoogleBinaryBlockHeader */
#include <tss_constants.h>
#include <vboot_api.h>
#ifdef CONFIG_SYS_COREBOOT
#include <asm/arch/sysinfo.h>
#endif
/*
* The current design of twostop firmware, if we use x86 firmware design as a
* metaphor, twostop firmware has:
* - One bootstub that select one of the main firmware
* - One read-only main firmware which can do recovery and normal/dev boot
* - Two readwrite main firmware which are virtually identical to x86 readwrite
* firmware, that is, they only have code path to normal/dev boot
*
* The readwrite main firmware does not reinitialize itself (this differs to the
* prior twostop design). As a consequence, a fixed protocol between bootstub
* and readwrite main firmware must be defined, specifying which hardware need
* or need not be initialized, what parameters are passed from bootstub to main
* firmware, and etc.
*
* The parameters are:
* - VbSharedData
* - GBB
* - Crossystem data
* Note that the format of the parameters must be versioned so that newer
* readwrite firmware can still work with old bootstub.
*/
/*
* TODO The current readwrite firmware is a full-fledged U-Boot. As a
* consequence, it will reinitialize most of the device that the bootstub
* already initialized. We should eliminate such reinitialization not just
* because it is slow, but also because it could be problematic.
*
* Given that, we must define a clear protocol specifying which device are
* initialized by the bootstub, and which are by the readwrite firmware.
*/
DECLARE_GLOBAL_DATA_PTR;
/* The margin to keep extra stack region that not to be wiped. */
#define STACK_MARGIN 1024
/*
* Combine VbSelectFirmware_t with VbError_t for this one file.
* TODO(wfrichar): Clean this up, either by changing vboot or refactoring here.
*/
enum {
/* VbSelectFirmware_t */
TWOSTOP_SELECT_FIRMWARE_RECOVERY = VB_SELECT_FIRMWARE_RECOVERY,
TWOSTOP_SELECT_FIRMWARE_A = VB_SELECT_FIRMWARE_A,
TWOSTOP_SELECT_FIRMWARE_B = VB_SELECT_FIRMWARE_B,
TWOSTOP_SELECT_FIRMWARE_READONLY = VB_SELECT_FIRMWARE_READONLY,
/* More choices */
TWOSTOP_SELECT_ERROR,
TWOSTOP_SELECT_POWER_OFF,
TWOSTOP_SELECT_COMMAND_LINE
};
/*
* TODO(sjg@chromium.org)
*
* This is non-zero if we have read the BMP block into our gbb region.
* This provides a work-around to allow us to read the BMP block when we
* think it will be needed. The correct solution is to have vboot ask for
* particular fragments of the GBB as needed. TBD.
*/
static int have_read_gbb_bmp_block;
static void *static_gbb; /* Pointer to GBB data */
static struct twostop_fmap fmap;
int cros_cboot_twostop_read_bmp_block(void)
{
/* Yet another use of this evil #define */
#ifndef CONFIG_HARDWARE_MAPPED_SPI
firmware_storage_t file;
int ret;
if (have_read_gbb_bmp_block)
return 0;
if (!fmap.readonly.fmap.length &&
cros_fdtdec_flashmap(gd->fdt_blob, &fmap)) {
VBDEBUG("failed to decode fmap\n");
return -1;
}
if (firmware_storage_open_spi(&file)) {
VBDEBUG("failed to open firmware storage\n");
return -1;
}
ret = gbb_read_bmp_block(static_gbb, &file, fmap.readonly.gbb.offset,
fmap.readonly.gbb.length);
file.close(&file);
if (ret)
return -1;
have_read_gbb_bmp_block = 1;
#endif /* CONFIG_HARDWARE_MAPPED_SPI */
return 0;
}
#if defined(VBOOT_DEBUG) || defined(DEBUG)
#define MY_ENUM_TO_STR(a) #a
static const char *
str_selection(uint32_t selection)
{
switch (selection) {
case TWOSTOP_SELECT_FIRMWARE_RECOVERY:
return MY_ENUM_TO_STR(TWOSTOP_SELECT_FIRMWARE_RECOVERY);
break;
case TWOSTOP_SELECT_FIRMWARE_A:
return MY_ENUM_TO_STR(TWOSTOP_SELECT_FIRMWARE_A);
break;
case TWOSTOP_SELECT_FIRMWARE_B:
return MY_ENUM_TO_STR(TWOSTOP_SELECT_FIRMWARE_B);
break;
case TWOSTOP_SELECT_FIRMWARE_READONLY:
return MY_ENUM_TO_STR(TWOSTOP_SELECT_FIRMWARE_READONLY);
break;
case TWOSTOP_SELECT_ERROR:
return MY_ENUM_TO_STR(TWOSTOP_SELECT_ERROR);
break;
case TWOSTOP_SELECT_POWER_OFF:
return MY_ENUM_TO_STR(TWOSTOP_SELECT_POWER_OFF);
break;
case TWOSTOP_SELECT_COMMAND_LINE:
return MY_ENUM_TO_STR(TWOSTOP_SELECT_COMMAND_LINE);
break;
}
return "<UNKNOWN>";
}
#undef MY_ENUM_TO_STR
#endif /* VBOOT_DEBUG || DEBUG */
/*
* Implement a weak default function for boards that optionally
* need to initialize the USB stack to detect their keyboard.
*/
int __board_use_usb_keyboard(void)
{
/* default: no USB keyboard as primary input */
return 0;
}
int board_use_usb_keyboard(int boot_mode)
__attribute__((weak, alias("__board_use_usb_keyboard")));
/*
* Check if two stop boot sequence can be interrupted. If configured - use the
* device tree contents to determine it. Some other means (like checking the
* environment) could be added later.
*
* Returns VB_INIT_FLAG_RO_NORMAL_SUPPORT if interruption is allowed or 0
* otherwise.
*/
static int check_ro_normal_support(void)
{
int rc = 0;
#ifdef CONFIG_OF_CONTROL
if (cros_fdtdec_config_has_prop(gd->fdt_blob,
"twostop-optional"))
rc = VB_INIT_FLAG_RO_NORMAL_SUPPORT;
#endif
VBDEBUG("%stwostop-optional\n", rc ? "" : "not ");
return rc;
}
static int
twostop_init_cparams(struct twostop_fmap *fmap, void *gbb,
void *vb_shared_data, VbCommonParams *cparams)
{
cparams->gbb_data = gbb;
cparams->gbb_size = fmap->readonly.gbb.length;
#ifdef CONFIG_SYS_COREBOOT
cparams->shared_data_blob =
&((chromeos_acpi_t *)lib_sysinfo.vdat_addr)->vdat;
cparams->shared_data_size =
sizeof(((chromeos_acpi_t *)lib_sysinfo.vdat_addr)->vdat);
#else
cparams->shared_data_blob = vb_shared_data;
cparams->shared_data_size = VB_SHARED_DATA_REC_SIZE;
#endif
#define P(format, field) \
VBDEBUG("- %-20s: " format "\n", #field, cparams->field)
VBDEBUG("cparams:\n");
P("%p", gbb_data);
P("%08x", gbb_size);
P("%p", shared_data_blob);
P("%08x", shared_data_size);
#undef P
return 0;
}
#if defined(CONFIG_OF_CONTROL) && defined(CONFIG_ARM)
#ifdef CONFIG_LCD
static int lcd_fb_size(void)
{
return panel_info.vl_row * panel_info.vl_col *
NBITS(panel_info.vl_bpix) / 8;
}
#endif
extern uint8_t _start;
extern uint8_t __bss_end__;
static void setup_arch_unused_memory(memory_wipe_t *wipe,
crossystem_data_t *cdata, VbCommonParams *cparams)
{
struct fdt_memory config, ramoops, lp0;
if (cros_fdtdec_memory(gd->fdt_blob, "/memory", &config))
VbExError("FDT decode memory section error\n");
memory_wipe_add(wipe, config.start, config.end);
#if !(defined(CONFIG_SYS_ICACHE_OFF) && defined(CONFIG_SYS_DCACHE_OFF))
/* Exclude the TLB */
memory_wipe_sub(wipe, gd->tlb_addr, gd->tlb_addr + gd->tlb_size);
#endif
/* 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);
#ifdef CONFIG_LCD
{
/* Excludes the frame buffer. */
int fb_size = lcd_fb_size();
memory_wipe_sub(wipe,
(uintptr_t)gd->fb_base,
(uintptr_t)gd->fb_base + fb_size);
}
#endif
}
#elif defined(CONFIG_SYS_COREBOOT)
extern uint8_t __text_start;
extern uint8_t __bss_end;
static void setup_arch_unused_memory(memory_wipe_t *wipe,
crossystem_data_t *cdata, VbCommonParams *cparams)
{
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
#if !defined(CONFIG_SANDBOX)
static void setup_arch_unused_memory(memory_wipe_t *wipe,
crossystem_data_t *cdata, VbCommonParams *cparams)
{
/* TODO(thutt@chromium.org): add memory wipe capability */
VBDEBUG("No memory wipe performed!");
}
#endif
#endif
#if !defined(CONFIG_SANDBOX)
static uintptr_t get_current_sp(void)
{
uintptr_t addr;
addr = (uintptr_t)&addr;
return addr;
}
#endif
static void wipe_unused_memory(crossystem_data_t *cdata,
VbCommonParams *cparams)
{
#if !defined(CONFIG_SANDBOX)
memory_wipe_t wipe;
int fdt_size __maybe_unused;
fdt_size = fdt_totalsize(gd->fdt_blob);
memory_wipe_init(&wipe);
setup_arch_unused_memory(&wipe, cdata, cparams);
/* Exclude relocated u-boot structures. */
memory_wipe_sub(&wipe, get_current_sp() - STACK_MARGIN,
#if defined(CONFIG_SYS_COREBOOT)
(uintptr_t)(gd->fdt_blob + fdt_size)
#elif defined(CONFIG_OF_CONTROL) && defined(CONFIG_ARM)
gd->relocaddr + (&__bss_end__ - &_start)
#else
#error Unknown compilation mode: text start & end unknown.
0
#endif
);
/* Exclude the shared data between bootstub and main firmware. */
memory_wipe_sub(&wipe, (uintptr_t)cdata,
(uintptr_t)cdata + sizeof(*cdata));
memory_wipe_sub(&wipe, (uintptr_t)cparams->gbb_data,
(uintptr_t)cparams->gbb_data + cparams->gbb_size);
memory_wipe_execute(&wipe);
#endif
}
/* Request the EC reboot to RO when the AP shuts down. */
static int request_ec_reboot_to_ro(void)
{
#ifdef CONFIG_MKBP
struct mkbp_dev *mdev = board_get_mkbp_dev();
if (!mdev) {
VBDEBUG("%s: no mkbp device: cannot request EC reboot to RO\n",
__func__);
return -1;
}
return mkbp_reboot(mdev, EC_REBOOT_COLD,
EC_REBOOT_FLAG_ON_AP_SHUTDOWN);
#else
return 0;
#endif
}
static int flash_sw_wp_is_enabled(firmware_storage_t *file)
{
uint8_t yes_it_is = 0;
#ifdef CONFIG_SPI_FLASH
int r = 0;
r = spi_flash_read_sw_wp_status(file->context, &yes_it_is);
if (r) {
VBDEBUG("spi_flash_read_sw_wp_status() failed: %d\n", r);
return 0;
}
#endif
VBDEBUG("flash SW WP is %d\n", yes_it_is);
return yes_it_is;
}
/* Fill in active EC firmware information. */
static int set_active_ec_firmware(crossystem_data_t* cdata)
{
int in_rw = 0;
int rv;
/* If software sync is disabled, just leave this as original value. */
if (!cros_fdtdec_config_has_prop(gd->fdt_blob, "ec-software-sync")) {
cdata->active_ec_firmware = ACTIVE_EC_FIRMWARE_UNCHANGE;
return 0;
}
rv = VbExEcRunningRW(&in_rw);
if (rv != VBERROR_SUCCESS)
return rv;
cdata->active_ec_firmware = (in_rw ? ACTIVE_EC_FIRMWARE_RW :
ACTIVE_EC_FIRMWARE_RO);
return 0;
}
static VbError_t
twostop_init_vboot_library(firmware_storage_t *file, void *gbb,
uint32_t gbb_offset, size_t gbb_size,
crossystem_data_t *cdata, VbCommonParams *cparams)
{
VbError_t err;
VbInitParams iparams;
int virtual_dev_switch =
cros_fdtdec_config_has_prop(gd->fdt_blob,
"virtual-dev-switch");
#ifdef CONFIG_MKBP
struct mkbp_dev *mdev = board_get_mkbp_dev();
#endif
memset(&iparams, 0, sizeof(iparams));
iparams.flags = check_ro_normal_support();
#ifdef CONFIG_MKBP
if (mdev) {
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 (mkbp_get_host_events(mdev, &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 (mkbp_clear_host_events(mdev, kb_rec_mask))
VBDEBUG("VbInit: unable to clear "
"EC KB recovery event\n");
}
}
#endif
if (cdata->boot_write_protect_switch)
iparams.flags |= VB_INIT_FLAG_WP_ENABLED;
if (cdata->boot_recovery_switch)
iparams.flags |= VB_INIT_FLAG_REC_BUTTON_PRESSED;
if (cdata->boot_developer_switch)
iparams.flags |= VB_INIT_FLAG_DEV_SWITCH_ON;
if (cdata->boot_oprom_loaded)
iparams.flags |= VB_INIT_FLAG_OPROM_LOADED;
if (cdata->oprom_matters)
iparams.flags |= VB_INIT_FLAG_OPROM_MATTERS;
if (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 (flash_sw_wp_is_enabled(file))
iparams.flags |= VB_INIT_FLAG_SW_WP_ENABLED;
VBDEBUG("iparams.flags: %08x\n", iparams.flags);
if ((err = VbInit(cparams, &iparams))) {
VBDEBUG("VbInit: %u\n", err);
/*
* If vboot wants EC to reboot to RO, make request now,
* because there isn't a clear path to pass this request
* through to do_vboot_twostop().
*/
if (err == VBERROR_EC_REBOOT_TO_RO_REQUIRED)
request_ec_reboot_to_ro();
return err;
}
#ifdef CONFIG_VIDEO_TEGRA
tegra_lcd_check_next_stage(gd->fdt_blob, 0);
#endif
#ifdef CONFIG_EXYNOS_DISPLAYPORT
exynos_lcd_check_next_stage(gd->fdt_blob, 0);
#endif
VBDEBUG("iparams.out_flags: %08x\n", iparams.out_flags);
if (virtual_dev_switch) {
cdata->boot_developer_switch =
(iparams.out_flags & VB_INIT_OUT_ENABLE_DEVELOPER) ?
1 : 0;
VBDEBUG("cdata->boot_developer_switch=%d\n",
cdata->boot_developer_switch);
}
if (iparams.out_flags & VB_INIT_OUT_CLEAR_RAM)
wipe_unused_memory(cdata, cparams);
/* Load required information of GBB */
if (iparams.out_flags & VB_INIT_OUT_ENABLE_DISPLAY) {
if (gbb_read_bmp_block(gbb, file, gbb_offset, gbb_size))
return VBERROR_INVALID_GBB;
have_read_gbb_bmp_block = 1;
}
if (cdata->boot_developer_switch ||
iparams.out_flags & VB_INIT_OUT_ENABLE_RECOVERY) {
if (gbb_read_recovery_key(gbb, file, gbb_offset, gbb_size))
return VBERROR_INVALID_GBB;
}
return VBERROR_SUCCESS;
}
static uint32_t
twostop_make_selection(struct twostop_fmap *fmap, firmware_storage_t *file,
VbCommonParams *cparams, void **fw_blob_ptr,
uint32_t *fw_size_ptr,
struct fmap_firmware_entry **entryp)
{
uint32_t selection = TWOSTOP_SELECT_ERROR;
#if !defined(CONFIG_SANDBOX)
VbError_t err;
#endif
uint32_t vlength;
VbSelectFirmwareParams fparams;
hasher_state_t s;
*entryp = NULL;
memset(&fparams, '\0', sizeof(fparams));
vlength = fmap->readwrite_a.vblock.length;
assert(vlength == fmap->readwrite_b.vblock.length);
fparams.verification_size_A = fparams.verification_size_B = vlength;
#ifndef CONFIG_HARDWARE_MAPPED_SPI
fparams.verification_block_A = cros_memalign_cache(vlength);
if (!fparams.verification_block_A) {
VBDEBUG("failed to allocate vblock A\n");
goto out;
}
fparams.verification_block_B = cros_memalign_cache(vlength);
if (!fparams.verification_block_B) {
VBDEBUG("failed to allocate vblock B\n");
goto out;
}
#endif
if (file->read(file, fmap->readwrite_a.vblock.offset, vlength,
BT_EXTRA fparams.verification_block_A)) {
VBDEBUG("fail to read vblock A\n");
goto out;
}
if (file->read(file, fmap->readwrite_b.vblock.offset, vlength,
BT_EXTRA fparams.verification_block_B)) {
VBDEBUG("fail to read vblock B\n");
goto out;
}
s.fw[0].vblock = fparams.verification_block_A;
s.fw[1].vblock = fparams.verification_block_B;
s.fw[0].offset = fmap->readwrite_a.boot.offset;
s.fw[1].offset = fmap->readwrite_b.boot.offset;
s.fw[0].size = fmap->readwrite_a.boot.length;
s.fw[1].size = fmap->readwrite_b.boot.length;
#ifndef CONFIG_HARDWARE_MAPPED_SPI
s.fw[0].cache = cros_memalign_cache(s.fw[0].size);
if (!s.fw[0].cache) {
VBDEBUG("failed to allocate cache A\n");
goto out;
}
s.fw[1].cache = cros_memalign_cache(s.fw[1].size);
if (!s.fw[1].cache) {
VBDEBUG("failed to allocate cache B\n");
goto out;
}
#endif
s.file = file;
cparams->caller_context = &s;
#if defined(CONFIG_SANDBOX)
fparams.verification_block_A = NULL;
fparams.verification_size_A = 0;
fparams.verification_block_B = NULL;
fparams.verification_size_B = 0;
fparams.selected_firmware = VB_SELECT_FIRMWARE_A;
#else
if ((err = VbSelectFirmware(cparams, &fparams))) {
VBDEBUG("VbSelectFirmware: %d\n", err);
/*
* If vboot wants EC to reboot to RO, make request now,
* because there isn't a clear path to pass this request
* through to do_vboot_twostop().
*/
if (err == VBERROR_EC_REBOOT_TO_RO_REQUIRED)
request_ec_reboot_to_ro();
goto out;
}
#endif
VBDEBUG("selected_firmware: %d\n", fparams.selected_firmware);
selection = fparams.selected_firmware;
out:
FREE_IF_NEEDED(fparams.verification_block_A);
FREE_IF_NEEDED(fparams.verification_block_B);
if (selection == VB_SELECT_FIRMWARE_A) {
uint32_t offset = fmap->readwrite_a.boot_rwbin.offset -
fmap->readwrite_a.boot.offset;
*fw_blob_ptr = s.fw[0].cache + offset;
*fw_size_ptr = s.fw[0].size - offset;
*entryp = &fmap->readwrite_a;
FREE_IF_NEEDED(s.fw[1].cache);
} else if (selection == VB_SELECT_FIRMWARE_B) {
uint32_t offset = fmap->readwrite_b.boot_rwbin.offset -
fmap->readwrite_b.boot.offset;
*fw_blob_ptr = s.fw[1].cache + offset;
*fw_size_ptr = s.fw[1].size - offset;
*entryp = &fmap->readwrite_b;
FREE_IF_NEEDED(s.fw[0].cache);
}
return selection;
}
static uint32_t
twostop_select_and_set_main_firmware(struct twostop_fmap *fmap,
firmware_storage_t *file, void *gbb,
size_t gbb_size, crossystem_data_t *cdata,
void *vb_shared_data, int *boot_mode,
void **fw_blob_ptr, uint32_t *fw_size_ptr,
struct fmap_firmware_entry **entryp)
{
uint32_t selection;
uint32_t id_offset = 0, id_length = 0;
int firmware_type;
struct fmap_firmware_entry *entry;
#ifndef CONFIG_HARDWARE_MAPPED_SPI
uint8_t firmware_id[ID_LEN];
#else
uint8_t *firmware_id;
#endif
VbCommonParams cparams;
*entryp = NULL;
bootstage_mark_name(BOOTSTAGE_VBOOT_SELECT_AND_SET,
"twostop_select_and_set_main_firmware");
if (twostop_init_cparams(fmap, gbb, vb_shared_data, &cparams)) {
VBDEBUG("failed to init cparams\n");
return TWOSTOP_SELECT_ERROR;
}
if (twostop_init_vboot_library(file, gbb, fmap->readonly.gbb.offset,
gbb_size, cdata, &cparams)
!= VBERROR_SUCCESS) {
VBDEBUG("failed to init vboot library\n");
return TWOSTOP_SELECT_ERROR;
}
/*
* TODO(sjg@chromium.org): Ick. Should unify readwrte_a/b and
* readonly, and then we can use entry for all purposee.
*/
selection = twostop_make_selection(fmap, file, &cparams,
fw_blob_ptr, fw_size_ptr, &entry);
VBDEBUG("selection: %s\n", str_selection(selection));
if (selection == TWOSTOP_SELECT_ERROR)
return TWOSTOP_SELECT_ERROR;
switch(selection) {
case VB_SELECT_FIRMWARE_RECOVERY:
case VB_SELECT_FIRMWARE_READONLY:
id_offset = fmap->readonly.firmware_id.offset;
id_length = fmap->readonly.firmware_id.length;
break;
case VB_SELECT_FIRMWARE_A:
case VB_SELECT_FIRMWARE_B:
id_offset = entry->firmware_id.offset;
id_length = entry->firmware_id.length;
break;
default:
VBDEBUG("impossible selection value: %d\n", selection);
assert(0);
}
if (file->read(file, id_offset,
MIN(sizeof(firmware_id), id_length),
BT_EXTRA firmware_id)) {
VBDEBUG("failed to read active firmware id\n");
firmware_id[0] = '\0';
}
if (selection == VB_SELECT_FIRMWARE_RECOVERY)
firmware_type = FIRMWARE_TYPE_RECOVERY;
else if (cdata->boot_developer_switch)
firmware_type = FIRMWARE_TYPE_DEVELOPER;
else
firmware_type = FIRMWARE_TYPE_NORMAL;
*boot_mode = firmware_type;
VBDEBUG("active main firmware type : %d\n", firmware_type);
VBDEBUG("active main firmware id : \"%s\"\n", firmware_id);
if (crossystem_data_set_main_firmware(cdata,
firmware_type, firmware_id)) {
VBDEBUG("failed to set active main firmware\n");
return TWOSTOP_SELECT_ERROR;
}
*entryp = entry;
return selection;
}
#if !defined(CONFIG_SANDBOX)
static uint32_t
twostop_jump(crossystem_data_t *cdata, void *fw_blob, uint32_t fw_size,
struct fmap_firmware_entry *entry)
{
void *dest = (void *)CONFIG_SYS_TEXT_BASE;
VBDEBUG("jump to readwrite main firmware at %#x, pos %p, size %#x\n",
dest, fw_blob, fw_size);
/*
* TODO: This version of U-Boot must be loaded at a fixed location. It
* could be problematic if newer version U-Boot changed this address.
*/
switch (entry->compress) {
#ifdef CONFIG_LZO
case CROS_COMPRESS_LZO: {
uint unc_len;
int ret;
bootstage_start(BOOTSTAGE_ID_ACCUM_DECOMP, "decompress_image");
ret = lzop_decompress(fw_blob, fw_size, dest, &unc_len);
if (ret < 0) {
VBDEBUG("LZO: uncompress or overwrite error %d "
"- must RESET board to recover\n", ret);
return TWOSTOP_SELECT_ERROR;
}
bootstage_accum(BOOTSTAGE_ID_ACCUM_DECOMP);
break;
}
#endif
case CROS_COMPRESS_NONE:
memmove(dest, fw_blob, fw_size);
break;
default:
VBDEBUG("Unsupported compression type %d\n", entry->compress);
return TWOSTOP_SELECT_ERROR;
}
/*
* TODO We need to reach the Point of Unification here, but I am not
* sure whether the following function call flushes L2 cache or not. If
* it does, we should avoid that.
*/
cleanup_before_linux();
((void(*)(void))CONFIG_SYS_TEXT_BASE)();
/* It is an error if readwrite firmware returns */
return TWOSTOP_SELECT_ERROR;
}
#endif
static int
twostop_init(struct twostop_fmap *fmap, firmware_storage_t *file,
void **gbbp, size_t gbb_size, crossystem_data_t *cdata,
void *vb_shared_data)
{
struct vboot_flag_details wpsw, recsw, devsw, oprom;
GoogleBinaryBlockHeader *gbbh;
uint8_t hardware_id[ID_LEN];
#ifndef CONFIG_HARDWARE_MAPPED_SPI
uint8_t readonly_firmware_id[ID_LEN];
#else
uint8_t *readonly_firmware_id;
#endif
int oprom_matters = 0;
int ret = -1;
void *gbb;
bootstage_mark_name(BOOTSTAGE_VBOOT_TWOSTOP_INIT, "twostop_init");
if (vboot_flag_fetch(VBOOT_FLAG_WRITE_PROTECT, &wpsw) ||
vboot_flag_fetch(VBOOT_FLAG_RECOVERY, &recsw) ||
vboot_flag_fetch(VBOOT_FLAG_DEVELOPER, &devsw) ||
vboot_flag_fetch(VBOOT_FLAG_OPROM_LOADED, &oprom)) {
VBDEBUG("failed to fetch gpio\n");
return -1;
}
vboot_flag_dump(VBOOT_FLAG_WRITE_PROTECT, &wpsw);
vboot_flag_dump(VBOOT_FLAG_RECOVERY, &recsw);
vboot_flag_dump(VBOOT_FLAG_DEVELOPER, &devsw);
vboot_flag_dump(VBOOT_FLAG_OPROM_LOADED, &oprom);
if (cros_fdtdec_config_has_prop(gd->fdt_blob, "oprom-matters")) {
VBDEBUG("FDT says oprom-matters\n");
oprom_matters = 1;
}
if (!fmap->readonly.fmap.length &&
cros_fdtdec_flashmap(gd->fdt_blob, fmap)) {
VBDEBUG("failed to decode fmap\n");
return -1;
}
dump_fmap(fmap);
/* We revert the decision of using firmware_storage_open_twostop() */
if (firmware_storage_open_spi(file)) {
VBDEBUG("failed to open firmware storage\n");
return -1;
}
/* Read read-only firmware ID */
if (file->read(file, fmap->readonly.firmware_id.offset,
MIN(sizeof(readonly_firmware_id),
fmap->readonly.firmware_id.length),
BT_EXTRA readonly_firmware_id)) {
VBDEBUG("failed to read firmware ID\n");
readonly_firmware_id[0] = '\0';
}
VBDEBUG("read-only firmware id: \"%s\"\n", readonly_firmware_id);
/* Load basic parts of gbb blob */
#ifdef CONFIG_HARDWARE_MAPPED_SPI
if (gbb_init(gbbp, file, fmap->readonly.gbb.offset, gbb_size)) {
VBDEBUG("failed to read gbb\n");
goto out;
}
gbb = *gbbp;
#else
gbb = *gbbp;
if (gbb_init(gbb, file, fmap->readonly.gbb.offset, gbb_size)) {
VBDEBUG("failed to read gbb\n");
goto out;
}
#endif
gbbh = (GoogleBinaryBlockHeader *)gbb;
memcpy(hardware_id, gbb + gbbh->hwid_offset,
MIN(sizeof(hardware_id), gbbh->hwid_size));
VBDEBUG("hardware id: \"%s\"\n", hardware_id);
/* Initialize crossystem data */
/*
* TODO There is no readwrite EC firmware on our current ARM boards. But
* we should have a mechanism to probe (or acquire this information from
* the device tree) whether the active EC firmware is R/O or R/W.
*/
if (crossystem_data_init(cdata,
&wpsw, &recsw, &devsw, &oprom,
oprom_matters,
fmap->readonly.fmap.offset,
ACTIVE_EC_FIRMWARE_RO,
hardware_id,
readonly_firmware_id)) {
VBDEBUG("failed to init crossystem data\n");
goto out;
}
ret = 0;
#ifdef CONFIG_VIDEO_TEGRA
tegra_lcd_check_next_stage(gd->fdt_blob, 0);
#endif
#ifdef CONFIG_EXYNOS_DISPLAYPORT
exynos_lcd_check_next_stage(gd->fdt_blob, 0);
#endif
out:
if (ret)
file->close(file);
return ret;
}
static uint32_t
twostop_main_firmware(struct twostop_fmap *fmap, void *gbb,
crossystem_data_t *cdata, void *vb_shared_data)
{
VbError_t err;
VbSelectAndLoadKernelParams kparams;
VbCommonParams cparams;
size_t size = 0;
#ifdef CONFIG_BOOTSTAGE_STASH
bootstage_unstash((void *)CONFIG_BOOTSTAGE_STASH,
CONFIG_BOOTSTAGE_STASH_SIZE);
#endif
bootstage_mark_name(BOOTSTAGE_VBOOT_TWOSTOP_MAIN_FIRMWARE,
"twostop_main_firmware");
if (twostop_init_cparams(fmap, gbb, vb_shared_data, &cparams)) {
VBDEBUG("failed to init cparams\n");
return TWOSTOP_SELECT_ERROR;
}
/*
* Note that in case "kernel" is not found in the device tree, the
* "size" value is going to remain unchanged.
*/
kparams.kernel_buffer = cros_fdtdec_alloc_region(gd->fdt_blob,
"kernel", &size);
kparams.kernel_buffer_size = size;
VBDEBUG("kparams:\n");
VBDEBUG("- kernel_buffer: : %p\n", kparams.kernel_buffer);
VBDEBUG("- kernel_buffer_size: : %08x\n",
kparams.kernel_buffer_size);
#ifdef CONFIG_EXYNOS_DISPLAYPORT
/*
* Make sure the LCD is up before we load the kernel. Partly this
* is because VbSelectAndLoadKernel may do a software sync.
*/
exynos_lcd_check_next_stage(gd->fdt_blob, 1);
#endif
if ((err = VbSelectAndLoadKernel(&cparams, &kparams))) {
VBDEBUG("VbSelectAndLoadKernel: %d\n", err);
switch (err) {
case VBERROR_SHUTDOWN_REQUESTED:
return TWOSTOP_SELECT_POWER_OFF;
case VBERROR_BIOS_SHELL_REQUESTED:
return TWOSTOP_SELECT_COMMAND_LINE;
case VBERROR_EC_REBOOT_TO_RO_REQUIRED:
request_ec_reboot_to_ro();
return TWOSTOP_SELECT_POWER_OFF;
}
return TWOSTOP_SELECT_ERROR;
}
VBDEBUG("kparams:\n");
VBDEBUG("- kernel_buffer: : %p\n", kparams.kernel_buffer);
VBDEBUG("- kernel_buffer_size: : %08x\n",
kparams.kernel_buffer_size);
VBDEBUG("- disk_handle: : %p\n", kparams.disk_handle);
VBDEBUG("- partition_number: : %08x\n",
kparams.partition_number);
VBDEBUG("- bootloader_address: : %08llx\n",
kparams.bootloader_address);
VBDEBUG("- bootloader_size: : %08x\n",
kparams.bootloader_size);
VBDEBUG("- partition_guid: :");
#ifdef VBOOT_DEBUG
int i;
for (i = 0; i < 16; i++)
VbExDebug(" %02x", kparams.partition_guid[i]);
VbExDebug("\n");
#endif /* VBOOT_DEBUG */
/* EC might jump between RO and RW during software sync. We need to
* update active EC copy in cdata. */
set_active_ec_firmware(cdata);
crossystem_data_dump(cdata);
#if defined(CONFIG_SANDBOX)
return TWOSTOP_SELECT_COMMAND_LINE;
#else
boot_kernel(&kparams, cdata);
/* It is an error if boot_kenel returns */
return TWOSTOP_SELECT_ERROR;
#endif
}
/**
* Get address of the cdata (and gbb, if not mapping SPI flash directly), and
* optionally verify them.
*
* @param gbb returns pointer to GBB when SPI flash is not mapped directly.
* Contains pointer to gbb otherwise.
* @param cdata returns pointer to crossystem data
* @param verify 1 to verify data, 0 to skip this step
* @return 0 if ok, -1 on error
*/
static int setup_gbb_and_cdata(void **gbb, size_t *gbb_size,
crossystem_data_t **cdata, int verify)
{
size_t size;
#ifndef CONFIG_HARDWARE_MAPPED_SPI
*gbb = cros_fdtdec_alloc_region(gd->fdt_blob,
"google-binary-block", gbb_size);
if (!*gbb) {
VBDEBUG("google-binary-block missing "
"from fdt, or malloc failed\n");
return -1;
}
#endif
*cdata = cros_fdtdec_alloc_region(gd->fdt_blob,
"cros-system-data", &size);
if (!*cdata) {
VBDEBUG("cros-system-data missing "
"from fdt, or malloc failed\n");
return -1;
}
/*
* TODO(clchiou): readwrite firmware should check version of the data
* blobs
*/
if (verify && crossystem_data_check_integrity(*cdata)) {
VBDEBUG("invalid crossystem data\n");
return -1;
}
if (verify && gbb_check_integrity(*gbb)) {
VBDEBUG("invalid gbb at %p\n", *gbb);
return -1;
}
return 0;
}
static uint32_t
twostop_boot(int stop_at_select)
{
firmware_storage_t file;
crossystem_data_t *cdata = NULL;
void *gbb;
size_t gbb_size = 0;
void *vb_shared_data;
void *fw_blob = NULL;
uint32_t fw_size = 0;
uint32_t selection;
int boot_mode = FIRMWARE_TYPE_NORMAL;
struct fmap_firmware_entry *entry;
if (setup_gbb_and_cdata(&gbb, &gbb_size, &cdata, 0))
return TWOSTOP_SELECT_ERROR;
vb_shared_data = cdata->vb_shared_data;
if (twostop_init(&fmap, &file, &gbb, gbb_size, cdata,
vb_shared_data)) {
VBDEBUG("failed to init twostop boot\n");
return TWOSTOP_SELECT_ERROR;
}
static_gbb = gbb;
selection = twostop_select_and_set_main_firmware(&fmap, &file,
gbb, gbb_size, cdata, vb_shared_data,
&boot_mode, &fw_blob, &fw_size, &entry);
VBDEBUG("selection of bootstub: %s\n", str_selection(selection));
file.close(&file); /* We don't care even if it fails */
if (stop_at_select)
return selection;
/* Don't we bother to free(fw_blob) if there was an error? */
#if !defined(CONFIG_SANDBOX)
if (selection == TWOSTOP_SELECT_ERROR)
return TWOSTOP_SELECT_ERROR;
if (selection == VB_SELECT_FIRMWARE_A ||
selection == VB_SELECT_FIRMWARE_B)
return twostop_jump(cdata, fw_blob, fw_size, entry);
assert(selection == VB_SELECT_FIRMWARE_READONLY ||
selection == VB_SELECT_FIRMWARE_RECOVERY);
#endif
/*
* TODO: Now, load drivers for rec/normal/dev main firmware.
*/
#ifdef CONFIG_USB_KEYBOARD
if (board_use_usb_keyboard(boot_mode)) {
int cnt;
/* enumerate USB devices to find the keyboard */
cnt = usb_init();
if (cnt >= 0)
drv_usb_kbd_init();
}
#endif
VBDEBUG("boot_mode: %d\n", boot_mode);
selection = twostop_main_firmware(&fmap, gbb, cdata, vb_shared_data);
VBDEBUG("selection of read-only main firmware: %s\n",
str_selection(selection));
if (selection != TWOSTOP_SELECT_COMMAND_LINE)
return selection;
/*
* TODO: Now, load all other drivers, such as networking, as we are
* returning back to the command line.
*/
return TWOSTOP_SELECT_COMMAND_LINE;
}
static uint32_t
twostop_readwrite_main_firmware(void)
{
crossystem_data_t *cdata;
void *gbb;
size_t gbb_size;
if (!fmap.readonly.fmap.length &&
cros_fdtdec_flashmap(gd->fdt_blob, &fmap)) {
VBDEBUG("failed to decode fmap\n");
return TWOSTOP_SELECT_ERROR;
}
dump_fmap(&fmap);
#ifdef CONFIG_HARDWARE_MAPPED_SPI
gbb = (void *) (fmap.readonly.gbb.offset + fmap.flash_base);
#endif
if (setup_gbb_and_cdata(&gbb, &gbb_size, &cdata, 1))
return TWOSTOP_SELECT_ERROR;
static_gbb = gbb;
#ifdef CONFIG_ARM
uint8_t ro_nvtype = cdata->board.arm.nonvolatile_context_storage;
/*
* Default to disk for older RO firmware which does not provide
* storage type.
*/
if (ro_nvtype == NONVOLATILE_STORAGE_NONE)
ro_nvtype = NONVOLATILE_STORAGE_DISK;
if (nvstorage_set_type(ro_nvtype))
return TWOSTOP_SELECT_ERROR;
cdata->board.arm.nonvolatile_context_storage = ro_nvtype;
#endif /* CONFIG_ARM */
/*
* VbSelectAndLoadKernel() assumes the TPM interface has already been
* initialized by VbSelectFirmware(). Since we haven't called
* VbSelectFirmware() in the readwrite firmware, we need to explicitly
* initialize the TPM interface. Note that this only re-initializes the
* interface, not the TPM itself.
*/
if (VbExTpmInit() != TPM_SUCCESS) {
VBDEBUG("failed to init tpm interface\n");
return TWOSTOP_SELECT_ERROR;
}
/* TODO Now, initialize device that bootstub did not initialize */
return twostop_main_firmware(&fmap, gbb, cdata, cdata->vb_shared_data);
}
/* FIXME(wfrichar): Work in progress. crosbug.com/p/11215 */
/* Write-protect portions of the RW flash until the next boot. */
VbError_t VbExProtectFlash(enum VbProtectFlash_t region)
{
#ifdef CONFIG_CAN_PROTECT_RW_FLASH
switch (region) {
case VBPROTECT_RW_A:
VBDEBUG("VBPROTECT_RW_A => 0x%08x 0x%x\n",
fmap.readwrite_a.all.offset,
fmap.readwrite_a.all.length);
spi_write_protect_region(fmap.readwrite_a.all.offset,
fmap.readwrite_a.all.length, 0);
break;
case VBPROTECT_RW_B:
VBDEBUG("VBPROTECT_RW_B => 0x%08x 0x%x\n",
fmap.readwrite_b.all.offset,
fmap.readwrite_b.all.length);
spi_write_protect_region(fmap.readwrite_b.all.offset,
fmap.readwrite_b.all.length, 0);
break;
case VBPROTECT_RW_DEVKEY:
VBDEBUG("VBPROTECT_RW_DEVKEY => 0x%08x 0x%x\n",
fmap.readwrite_devkey.offset,
fmap.readwrite_devkey.length);
spi_write_protect_region(fmap.readwrite_devkey.offset,
fmap.readwrite_devkey.length, 1);
break;
default:
VBDEBUG("unknown region %d\n", region);
return VBERROR_INVALID_PARAMETER;
}
return VBERROR_SUCCESS;
#else
VBDEBUG("%s not implemented on this platform\n", __func__);
return VBERROR_UNKNOWN;
#endif
}
static int
do_vboot_twostop(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
uint32_t selection;
int ro_firmware;
bootstage_mark_name(BOOTSTAGE_VBOOT_TWOSTOP, "do_vboot_twostop");
/*
* Empty keyboard buffer before boot. In case EC did not clear its
* buffer between power cycles, this prevents vboot of current power
* cycle being affected by keystrokes of previous power cycle.
*/
while (tstc())
getc();
if (cros_init()) {
VBDEBUG("fail to init cros library\n");
goto on_error;
}
/*
* TODO: We should clear screen later if we load graphics optionally.
* In normal mode, we don't need to load graphics driver and clear
* screen.
*/
display_clear();
/*
* TODO(sjg@chromium.org): root cause issue crosbug.com/p/11075
*
* Ensure there are no keys in the keyboard buffer, so that we don't
* accidentally see a space key and go into recovery mode.
*/
while (tstc())
(void)getc();
/*
* A processor reset jumps to the reset entry point (which is the
* read-only firmware), otherwise we have entered U-Boot from a
* software jump.
*
* Note: If a read-only firmware is loaded to memory not because of a
* processor reset, this instance of read-only firmware should go to the
* readwrite firmware code path.
*/
ro_firmware = is_processor_reset();
VBDEBUG("Starting %s firmware\n", ro_firmware ? "read-only" :
"read-write");
if (ro_firmware)
selection = twostop_boot(0);
else
selection = twostop_readwrite_main_firmware();
VBDEBUG("selection of main firmware: %s\n",
str_selection(selection));
if (selection == TWOSTOP_SELECT_COMMAND_LINE)
return 0;
if (selection == TWOSTOP_SELECT_POWER_OFF)
power_off();
assert(selection == TWOSTOP_SELECT_ERROR);
on_error:
cold_reboot();
return 0;
}
U_BOOT_CMD(vboot_twostop, 1, 1, do_vboot_twostop,
"verified boot twostop firmware", NULL);
static int
do_vboot_load_oprom(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
{
uint32_t selection;
struct vboot_flag_details oprom;
if (cros_init()) {
VBDEBUG("fail to init cros library\n");
return -1;
}
/* We should be in RO now. */
if (!is_processor_reset()) {
VBDEBUG("This command should only be executed in RO.\n");
return -1;
}
if (!cros_fdtdec_config_has_prop(gd->fdt_blob, "oprom-matters")) {
VBDEBUG("FDT doesn't say oprom-matters.\n");
return -1;
}
if (vboot_flag_fetch(VBOOT_FLAG_OPROM_LOADED, &oprom)) {
VBDEBUG("Failed to fetch OPROM gpio\n");
return -1;
}
vboot_flag_dump(VBOOT_FLAG_OPROM_LOADED, &oprom);
if (oprom.value) {
VBDEBUG("OPROM already loaded\n");
return 0;
}
/*
* Initialize necessary data and stop at firmware selection. If
* OPROM is not loaded and is needed, we should get an error here.
*/
selection = twostop_boot(1);
if (selection == TWOSTOP_SELECT_ERROR) {
cold_reboot();
return 0;
} else {
VBDEBUG("Vboot doesn't say we need OPROM.\n");
return -1;
}
}
U_BOOT_CMD(vboot_load_oprom, 1, 1, do_vboot_load_oprom,
"load oprom if it is needed", NULL);