blob: 6ae53f17dcbefa4b9499f3302ea77459545e8244 [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* See file CREDITS for list of people who contributed to this
* project.
*
* 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; either version 2 of
* the License, or (at your option) any later version.
*
* 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., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
#include <assert.h>
#include <libpayload.h>
#include <stdint.h>
#include <vboot_api.h>
#include <vboot_nvstorage.h>
#include "base/timestamp.h"
#include "boot/commandline.h"
#include "config.h"
#include "drivers/flash/flash.h"
#include "drivers/input/input.h"
#include "drivers/power/power.h"
#include "drivers/storage/blockdev.h"
#include "image/fmap.h"
#include "image/index.h"
#include "image/startrw.h"
#include "image/symbols.h"
#include "vboot/boot.h"
#include "vboot/boot_policy.h"
#include "vboot/stages.h"
#include "vboot/crossystem/crossystem.h"
#include "vboot/util/commonparams.h"
#include "vboot/util/ec.h"
#include "vboot/util/flag.h"
#include "vboot/util/memory.h"
static uint32_t vboot_out_flags;
int vboot_init(void)
{
VbInitParams iparams = {
.flags = 0
};
int dev_switch = flag_fetch(FLAG_DEVSW);
int rec_switch = flag_fetch(FLAG_RECSW);
int wp_switch = flag_fetch(FLAG_WPSW);
int lid_switch = flag_fetch(FLAG_LIDSW);
int oprom_loaded = 0;
if (CONFIG_OPROM_MATTERS)
oprom_loaded = flag_fetch(FLAG_OPROM);
printf("%s:%d dev %d, rec %d, wp %d, lid %d, oprom %d\n",
__func__, __LINE__, dev_switch, rec_switch,
wp_switch, lid_switch, oprom_loaded);
if (dev_switch < 0 || rec_switch < 0 || lid_switch < 0 ||
wp_switch < 0 || oprom_loaded < 0) {
// An error message should have already been printed.
return 1;
}
// Decide what flags to pass into VbInit.
iparams.flags |= VB_INIT_FLAG_RO_NORMAL_SUPPORT;
/* Don't fail the boot process when lid switch is closed.
* The OS might not have enough time to log success before
* shutting down.
*/
if (!lid_switch)
iparams.flags |= VB_INIT_FLAG_NOFAIL_BOOT;
if (dev_switch)
iparams.flags |= VB_INIT_FLAG_DEV_SWITCH_ON;
if (rec_switch)
iparams.flags |= VB_INIT_FLAG_REC_BUTTON_PRESSED;
if (wp_switch)
iparams.flags |= VB_INIT_FLAG_WP_ENABLED;
if (oprom_loaded)
iparams.flags |= VB_INIT_FLAG_OPROM_LOADED;
if (CONFIG_OPROM_MATTERS)
iparams.flags |= VB_INIT_FLAG_OPROM_MATTERS;
if (CONFIG_VIRTUAL_DEV_SWITCH)
iparams.flags |= VB_INIT_FLAG_VIRTUAL_DEV_SWITCH;
if (CONFIG_EC_SOFTWARE_SYNC)
iparams.flags |= VB_INIT_FLAG_EC_SOFTWARE_SYNC;
if (!CONFIG_PHYSICAL_REC_SWITCH)
iparams.flags |= VB_INIT_FLAG_VIRTUAL_REC_SWITCH;
printf("Calling VbInit().\n");
VbError_t res = VbInit(&cparams, &iparams);
if (res != VBERROR_SUCCESS) {
printf("VbInit returned %d, Doing a cold reboot.\n", res);
if (cold_reboot())
return 1;
}
return vboot_do_init_out_flags(iparams.out_flags);
};
int vboot_in_recovery(void)
{
return vboot_out_flags & VB_INIT_OUT_ENABLE_RECOVERY;
}
int vboot_in_developer(void)
{
return vboot_out_flags & VB_INIT_OUT_ENABLE_DEVELOPER;
}
void vboot_update_recovery(uint32_t request)
{
VbNvContext context;
VbExNvStorageRead(context.raw);
VbNvSetup(&context);
VbNvSet(&context, VBNV_RECOVERY_REQUEST, request);
VbNvTeardown(&context);
if (context.raw_changed)
VbExNvStorageWrite(context.raw);
}
int vboot_do_init_out_flags(uint32_t out_flags)
{
if (out_flags & VB_INIT_OUT_CLEAR_RAM) {
if (memory_wipe_unused())
return 1;
}
/*
* If in developer mode or recovery mode, assume we're going to need
* input. We'll want it up and responsive by the time we present
* prompts to the user, so get it going ahead of time.
*/
if (out_flags & (VB_INIT_OUT_ENABLE_DEVELOPER |
VB_INIT_OUT_ENABLE_RECOVERY))
input_enable();
vboot_out_flags = out_flags;
return 0;
}
int vboot_select_firmware(void)
{
VbSelectFirmwareParams fparams = {
.verification_block_A = NULL,
.verification_block_B = NULL,
.verification_size_A = 0,
.verification_size_B = 0
};
// Set up the fparams structure.
FmapArea vblock_a, vblock_b;
if (fmap_find_area("VBLOCK_A", &vblock_a) ||
fmap_find_area("VBLOCK_B", &vblock_b)) {
printf("Couldn't find one of the vblocks.\n");
return 1;
}
fparams.verification_block_A =
flash_read(vblock_a.offset, vblock_a.size);
fparams.verification_size_A = vblock_a.size;
fparams.verification_block_B =
flash_read(vblock_b.offset, vblock_b.size);
fparams.verification_size_B = vblock_b.size;
printf("Calling VbSelectFirmware().\n");
VbError_t res = VbSelectFirmware(&cparams, &fparams);
if (res != VBERROR_SUCCESS) {
printf("VbSelectFirmware returned %d, "
"Doing a cold reboot.\n", res);
if (cold_reboot())
return 1;
}
enum VbSelectFirmware_t select = fparams.selected_firmware;
// If an RW firmware was selected, start it.
if (select == VB_SELECT_FIRMWARE_A || select == VB_SELECT_FIRMWARE_B) {
const char *name;
if (select == VB_SELECT_FIRMWARE_A)
name = "FW_MAIN_A";
else
name = "FW_MAIN_B";
FmapArea rw_area;
if (fmap_find_area(name, &rw_area)) {
printf("Didn't find section %s in the fmap.\n", name);
return 1;
}
uint32_t image_size;
const void *image = index_subsection(&rw_area, 0, &image_size);
if (!image)
return 1;
if (start_rw_firmware(image, image_size))
return 1;
}
return 0;
}
int vboot_select_and_load_kernel(void)
{
VbSelectAndLoadKernelParams kparams = {
.kernel_buffer = &_kernel_start,
.kernel_buffer_size = &_kernel_end - &_kernel_start
};
printf("Calling VbSelectAndLoadKernel().\n");
VbError_t res = VbSelectAndLoadKernel(&cparams, &kparams);
if (res == VBERROR_EC_REBOOT_TO_RO_REQUIRED) {
printf("Rebooting the EC to RO.\n");
reboot_ec_to_ro();
if (power_off())
return 1;
} else if (res == VBERROR_SHUTDOWN_REQUESTED) {
printf("Powering off.\n");
if (power_off())
return 1;
}
if (res != VBERROR_SUCCESS) {
printf("VbSelectAndLoadKernel returned %d, "
"Doing a cold reboot.\n", res);
if (cold_reboot())
return 1;
}
vboot_boot_kernel(&kparams);
return 1;
}
void vboot_boot_kernel(VbSelectAndLoadKernelParams *kparams)
{
static char cmd_line_buf[2 * CmdLineSize];
struct boot_info bi;
timestamp_add_now(TS_CROSSYSTEM_DATA);
memset(&bi, 0, sizeof(bi));
if (fill_boot_info(&bi, kparams) == -1) {
printf("ERROR!!! Unable to parse boot info\n");
goto fail;
}
BlockDev *bdev = (BlockDev *)kparams->disk_handle;
struct commandline_info info = {
.devnum = 0,
.partnum = kparams->partition_number + 1,
.guid = kparams->partition_guid,
.external_gpt = bdev->external_gpt,
};
if (bi.cmd_line) {
if (commandline_subst(bi.cmd_line, cmd_line_buf,
sizeof(cmd_line_buf), &info))
return;
bi.cmd_line = cmd_line_buf;
}
if (crossystem_setup())
return;
boot(&bi);
fail:
/*
* If the boot succeeded we'd never end up here. If configured, let's
* try booting in alternative way.
*/
if (CONFIG_KERNEL_LEGACY)
legacy_boot(bi.kernel, cmd_line_buf);
}