| /* |
| * Copyright (c) 2011 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. |
| */ |
| |
| /* Implementation of boot stub of Chrome OS Verify Boot firmware */ |
| |
| #include <common.h> |
| #include <command.h> |
| #include <malloc.h> |
| #include <chromeos/common.h> |
| #include <chromeos/firmware_storage.h> |
| #include <chromeos/gpio.h> |
| #include <chromeos/kernel_shared_data.h> |
| #include <chromeos/load_firmware_helper.h> |
| #include <chromeos/power_management.h> |
| #include <chromeos/vboot_nvstorage_helper.h> |
| |
| /* Verify Boot interface */ |
| #include <gbb_header.h> |
| #include <load_firmware_fw.h> |
| #include <vboot_nvstorage.h> |
| #include <vboot_struct.h> |
| |
| #define PREFIX "cros_bootstub: " |
| |
| #define WARN_ON_FAILURE(action) do { \ |
| int return_code = (action); \ |
| if (return_code != 0) \ |
| printf(PREFIX "%s failed, returning %d\n", \ |
| #action, return_code); \ |
| } while (0) |
| |
| /* |
| * Read recovery firmware into <recovery_firmware_buffer>. |
| * |
| * Return 0 on success, non-zero on error. |
| */ |
| int load_recovery_firmware(firmware_storage_t *file, |
| uint8_t *recovery_firmware_buffer) |
| { |
| int retval; |
| |
| retval = firmware_storage_read(file, |
| CONFIG_OFFSET_RECOVERY, CONFIG_LENGTH_RECOVERY, |
| recovery_firmware_buffer); |
| if (retval) { |
| VBDEBUG(PREFIX "cannot load recovery firmware\n"); |
| } |
| |
| return retval; |
| } |
| |
| void jump_to_firmware(void (*firmware_entry_point)(void)) |
| { |
| VBDEBUG(PREFIX "jump to firmware %p\n", firmware_entry_point); |
| |
| cleanup_before_linux(); |
| |
| /* should never return! */ |
| firmware_entry_point(); |
| |
| /* FIXME(clchiou) Bring up a sad face as boot has failed */ |
| enable_interrupts(); |
| VBDEBUG(PREFIX "error: firmware returns\n"); |
| while (1); |
| } |
| |
| int do_cros_bootstub(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) |
| { |
| int status = LOAD_FIRMWARE_RECOVERY; |
| firmware_storage_t file; |
| VbNvContext nvcxt; |
| uint64_t boot_flags = 0; |
| uint32_t recovery_request = 0; |
| uint32_t reason = VBNV_RECOVERY_NOT_REQUESTED; |
| uint8_t *firmware_data; |
| |
| if (firmware_storage_init(&file)) { |
| /* FIXME(clchiou) Bring up a sad face as boot has failed */ |
| VBDEBUG(PREFIX "init_firmware_storage fail\n"); |
| while (1); |
| } |
| |
| if (is_firmware_write_protect_gpio_asserted()) |
| WARN_ON_FAILURE(file.lock_device(file.context)); |
| |
| /* Fill in the RO firmware ID */ |
| KernelSharedDataType *sd = get_kernel_shared_data(); |
| if (firmware_storage_read(&file, |
| (off_t)CONFIG_OFFSET_RO_FRID, |
| (size_t)CONFIG_LENGTH_RO_FRID, |
| sd->frid)) { |
| VBDEBUG(PREFIX "fail to read fwid\n"); |
| reason = VBNV_RECOVERY_US_UNSPECIFIED; |
| goto RECOVERY; |
| } |
| |
| if (read_nvcontext(&nvcxt) || VbNvGet(&nvcxt, VBNV_RECOVERY_REQUEST, |
| &recovery_request)) { |
| VBDEBUG(PREFIX "fail to read nvcontext\n"); |
| reason = VBNV_RECOVERY_US_UNSPECIFIED; |
| goto RECOVERY; |
| } |
| |
| /* clear VBNV_DEBUG_RESET_MODE after read */ |
| if (VbNvSet(&nvcxt, VBNV_DEBUG_RESET_MODE, 0)) { |
| VBDEBUG(PREFIX "fail to write nvcontext\n"); |
| reason = VBNV_RECOVERY_US_UNSPECIFIED; |
| goto RECOVERY; |
| } |
| |
| if (recovery_request != VBNV_RECOVERY_NOT_REQUESTED) { |
| VBDEBUG(PREFIX "boot recovery cookie set\n"); |
| reason = recovery_request; |
| goto RECOVERY; |
| } |
| |
| if (is_recovery_mode_gpio_asserted()) { |
| VBDEBUG(PREFIX "recovery button pressed\n"); |
| reason = VBNV_RECOVERY_RO_MANUAL; |
| goto RECOVERY; |
| } |
| |
| if (is_developer_mode_gpio_asserted()) |
| boot_flags |= BOOT_FLAG_DEVELOPER; |
| |
| status = load_firmware_wrapper(&file, |
| boot_flags, &nvcxt, NULL, &firmware_data); |
| |
| if (nvcxt.raw_changed && write_nvcontext(&nvcxt)) { |
| VBDEBUG(PREFIX "fail to write nvcontext\n"); |
| reason = VBNV_RECOVERY_US_UNSPECIFIED; |
| goto RECOVERY; |
| } |
| |
| if (status == LOAD_FIRMWARE_SUCCESS) { |
| jump_to_firmware((void (*)(void)) firmware_data); |
| } else if (status == LOAD_FIRMWARE_REBOOT) { |
| cros_reboot(); |
| } |
| |
| /* assert(status == LOAD_FIRMWARE_RECOVERY) */ |
| |
| RECOVERY: |
| VBDEBUG(PREFIX "write to recovery cookie\n"); |
| |
| /* |
| * Although writing back VbNvContext cookies may fail, we boot |
| * recovery firmware anyway. In this way, the recovery reason |
| * would be incorrect, but this is much better than not booting |
| * anything. |
| */ |
| |
| if (reason != VBNV_RECOVERY_NOT_REQUESTED && |
| VbNvSet(&nvcxt, VBNV_RECOVERY_REQUEST, reason)) { |
| /* FIXME: bring up a sad face? */ |
| VBDEBUG(PREFIX "error: cannot write recovery reason\n"); |
| } |
| |
| if (VbNvTeardown(&nvcxt)) { |
| /* FIXME: bring up a sad face? */ |
| VBDEBUG(PREFIX "error: cannot tear down cookie\n"); |
| } |
| |
| if (nvcxt.raw_changed && write_nvcontext(&nvcxt)) { |
| /* FIXME: bring up a sad face? */ |
| VBDEBUG(PREFIX "error: cannot write recovery cookie\n"); |
| } |
| |
| VBDEBUG(PREFIX "jump to recovery firmware and never return\n"); |
| |
| firmware_data = malloc(CONFIG_LENGTH_RECOVERY); |
| WARN_ON_FAILURE(load_recovery_firmware(&file, firmware_data)); |
| jump_to_firmware((void (*)(void)) firmware_data); |
| |
| /* never reach here */ |
| return 1; |
| } |
| |
| U_BOOT_CMD(cros_bootstub, 1, 1, do_cros_bootstub, "verified boot stub firmware", |
| NULL); |