/* * This file is part of the coreboot project. * * Copyright 2016 Google Inc. * * 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. */ #define NEED_VB20_INTERNALS /* Peeking into vb2_shared_data */ #include #include #include #include #include #include #include #include static int vboot_get_recovery_reason_shared_data(void) { struct vb2_shared_data *sd = vb2_get_sd(vboot_get_context()); assert(sd); return sd->recovery_reason; } void vboot_save_recovery_reason_vbnv(void) { if (!CONFIG(VBOOT_SAVE_RECOVERY_REASON_ON_REBOOT)) return; int reason = vboot_get_recovery_reason_shared_data(); if (!reason) return; set_recovery_mode_into_vbnv(reason); } static void vboot_clear_recovery_reason_vbnv(void *unused) { if (!CONFIG(VBOOT_SAVE_RECOVERY_REASON_ON_REBOOT)) return; set_recovery_mode_into_vbnv(0); } /* * Recovery reason stored in VBNV needs to be cleared before the state of VBNV * is backed-up anywhere or jumping to the payload (whichever occurs * first). Currently, vbnv_cmos.c backs up VBNV on POST_DEVICE. Thus, we need to * make sure that the stored recovery reason is cleared off before that * happens. * IMPORTANT: Any reboot occurring after BS_DEV_INIT state will cause loss of * recovery reason on reboot. Until now, we have seen reboots occurring on x86 * only in FSP stages which run before BS_DEV_INIT. */ BOOT_STATE_INIT_ENTRY(BS_DEV_INIT, BS_ON_EXIT, vboot_clear_recovery_reason_vbnv, NULL); /* * Returns 1 if vboot is being used and currently in a stage which might have * already executed vboot verification. */ static int vboot_possibly_executed(void) { if (CONFIG(VBOOT_STARTS_IN_BOOTBLOCK)) { if (ENV_BOOTBLOCK && CONFIG(VBOOT_SEPARATE_VERSTAGE)) return 0; return 1; } if (CONFIG(VBOOT_STARTS_IN_ROMSTAGE)) { if (ENV_BOOTBLOCK) return 0; return 1; } return 0; } /* * vb2_check_recovery_request looks up different components to identify if there * is a recovery request and returns appropriate reason code: * 1. Checks if recovery mode is initiated by EC. If yes, returns * VB2_RECOVERY_RO_MANUAL. * 2. Checks if recovery request is present in VBNV and returns the code read * from it. * 3. Checks if vboot verification is done and looks up selected region * to identify if vboot_reference library has requested recovery path. * If yes, return the reason code from shared data. * 4. If nothing applies, return 0 indicating no recovery request. */ int vboot_check_recovery_request(void) { int reason = 0; /* EC-initiated recovery. */ if (get_recovery_mode_switch()) return VB2_RECOVERY_RO_MANUAL; /* Recovery request in VBNV. */ if ((reason = get_recovery_mode_from_vbnv()) != 0) return reason; /* * Identify if vboot verification is already complete and no slot * was selected i.e. recovery path was requested. */ if (vboot_possibly_executed() && vboot_logic_executed() && !vboot_is_slot_selected()) return vboot_get_recovery_reason_shared_data(); return 0; } int vboot_recovery_mode_enabled(void) { return !!vboot_check_recovery_request(); } int __weak clear_recovery_mode_switch(void) { // Weak implementation. Nothing to do. return 0; } void __weak log_recovery_mode_switch(void) { // Weak implementation. Nothing to do. } int __weak get_recovery_mode_retrain_switch(void) { return 0; } int vboot_recovery_mode_memory_retrain(void) { return get_recovery_mode_retrain_switch(); } int vboot_developer_mode_enabled(void) { return cbmem_possibly_online() && vboot_get_working_data()->flags & VBOOT_WD_FLAG_DEVELOPER_MODE; } #if CONFIG(VBOOT_NO_BOARD_SUPPORT) /** * TODO: Create flash protection interface which implements get_write_protect_state. * get_recovery_mode_switch should be implemented as default function. */ int __weak get_write_protect_state(void) { return 0; } int __weak get_recovery_mode_switch(void) { return 0; } #endif