core/host_fw/host_processor_dual.c (272 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "host_fw_util.h"
#include "host_logging.h"
#include "host_processor_dual.h"
#include "host_state_manager.h"
#include "flash/flash_util.h"
#include "recovery/recovery_image.h"
/**
* Internal function to apply bypass mode.
*
* @param host The host to configure for bypass mode.
* @param swap_flash Flag to swap flash roles before configuring bypass mode.
*/
static void host_processor_dual_force_bypass_mode (struct host_processor_filtered *host,
bool swap_flash)
{
if (swap_flash) {
if (host_state_manager_get_read_only_flash (host->state) == SPI_FILTER_CS_0) {
host_state_manager_save_read_only_flash (host->state, SPI_FILTER_CS_1);
}
else {
host_state_manager_save_read_only_flash (host->state, SPI_FILTER_CS_0);
}
}
host_processor_filtered_config_bypass (host);
}
static int host_processor_dual_power_on_reset (struct host_processor *host,
const struct hash_engine *hash, const struct rsa_engine *rsa)
{
struct host_processor_filtered *dual = (struct host_processor_filtered*) host;
if (dual == NULL) {
return HOST_PROCESSOR_INVALID_ARGUMENT;
}
return host_processor_filtered_power_on_reset (dual, hash, rsa, false);
}
static int host_processor_dual_soft_reset (struct host_processor *host,
const struct hash_engine *hash, const struct rsa_engine *rsa)
{
struct host_processor_filtered *dual = (struct host_processor_filtered*) host;
if (dual == NULL) {
return HOST_PROCESSOR_INVALID_ARGUMENT;
}
return host_processor_filtered_update_verification (dual, hash, rsa, false, true, 0);
}
static int host_processor_dual_run_time_verification (struct host_processor *host,
const struct hash_engine *hash, const struct rsa_engine *rsa)
{
struct host_processor_filtered *dual = (struct host_processor_filtered*) host;
if (dual == NULL) {
return HOST_PROCESSOR_INVALID_ARGUMENT;
}
return host_processor_filtered_update_verification (dual, hash, rsa, false, false,
HOST_PROCESSOR_NOTHING_TO_VERIFY);
}
static int host_processor_dual_flash_rollback (struct host_processor *host,
const struct hash_engine *hash, const struct rsa_engine *rsa, bool disable_bypass,
bool no_reset)
{
struct host_processor_filtered *dual = (struct host_processor_filtered*) host;
const struct pfm *active_pfm;
struct host_flash_manager_rw_regions rw_list;
const struct spi_flash *ro_flash;
const struct spi_flash *rw_flash;
uint32_t dev_size;
int status = 0;
if ((dual == NULL) || (hash == NULL) || (rsa == NULL)) {
return HOST_PROCESSOR_INVALID_ARGUMENT;
}
platform_mutex_lock (&dual->lock);
debug_log_create_entry (DEBUG_LOG_SEVERITY_WARNING, DEBUG_LOG_COMPONENT_HOST_FW,
HOST_LOGGING_ROLLBACK_STARTED, dual->base.port, 0);
active_pfm = dual->pfm->get_active_pfm (dual->pfm);
if (active_pfm && !host_state_manager_is_flash_supported (dual->state)) {
status = HOST_PROCESSOR_FLASH_NOT_SUPPORTED;
goto exit;
}
if ((!active_pfm && !disable_bypass) ||
(active_pfm && (!host_state_manager_is_inactive_dirty (dual->state) ||
host_state_manager_is_bypass_mode (dual->state)))) {
if (host_state_manager_is_bypass_mode (dual->state) && disable_bypass) {
status = HOST_PROCESSOR_NO_ROLLBACK;
goto exit;
}
if (!no_reset && !dual->reset_pulse) {
dual->control->hold_processor_in_reset (dual->control, true);
}
status = dual->flash->set_flash_for_rot_access (dual->flash, dual->control);
if (status != 0) {
goto return_flash;
}
if (active_pfm) {
if (!host_state_manager_is_bypass_mode (dual->state)) {
/* Even though the dirty state hasn't been set, we still need to make sure the other
* flash contains a good image prior to activating it. */
status = dual->flash->validate_read_write_flash (dual->flash, active_pfm, hash, rsa,
&rw_list);
if (status == 0) {
host_processor_filtered_swap_flash (dual, &rw_list, NULL, true);
dual->flash->free_read_write_regions (dual->flash, &rw_list);
observable_notify_observers (&dual->base.observable,
offsetof (struct host_processor_observer, on_active_mode));
}
}
else {
/* If we are in forced bypass mode, just switch flashes. */
host_processor_dual_force_bypass_mode (dual, true);
}
}
else {
/* We are not in active mode yet, so just copy the contents from the second flash
* entirely into the boot flash. */
ro_flash = dual->flash->get_read_only_flash (dual->flash);
rw_flash = dual->flash->get_read_write_flash (dual->flash);
spi_flash_get_device_size (ro_flash, &dev_size);
status = spi_flash_chip_erase (ro_flash);
if (status != 0) {
goto return_flash;
}
status = flash_copy_ext_to_blank_and_verify (&ro_flash->base, 0, &rw_flash->base, 0,
dev_size);
}
return_flash:
host_processor_filtered_set_host_flash_access (dual);
if (!no_reset) {
if (dual->reset_pulse) {
dual->control->hold_processor_in_reset (dual->control, true);
platform_msleep (dual->reset_pulse);
}
dual->control->hold_processor_in_reset (dual->control, false);
}
}
else if (!active_pfm) {
status = HOST_PROCESSOR_NO_ROLLBACK;
}
else {
status = HOST_PROCESSOR_ROLLBACK_DIRTY;
}
exit:
if (active_pfm) {
dual->pfm->free_pfm (dual->pfm, active_pfm);
}
if (status == 0) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_HOST_FW,
HOST_LOGGING_ROLLBACK_COMPLETED, dual->base.port, 0);
}
else {
debug_log_create_entry (DEBUG_LOG_SEVERITY_ERROR, DEBUG_LOG_COMPONENT_HOST_FW,
HOST_LOGGING_ROLLBACK_FAILED, status, dual->base.port);
}
platform_mutex_unlock (&dual->lock);
return status;
}
static int host_processor_dual_recover_active_read_write_data (struct host_processor *host)
{
struct host_processor_filtered *dual = (struct host_processor_filtered*) host;
const struct pfm *active_pfm;
int status = HOST_PROCESSOR_NO_ACTIVE_RW_DATA;
if (dual == NULL) {
return HOST_PROCESSOR_INVALID_ARGUMENT;
}
if (!host_state_manager_is_bypass_mode (dual->state)) {
active_pfm = dual->pfm->get_active_pfm (dual->pfm);
if (active_pfm) {
if (!dual->reset_pulse) {
dual->control->hold_processor_in_reset (dual->control, true);
}
status = dual->flash->set_flash_for_rot_access (dual->flash, dual->control);
if (status == 0) {
status = host_processor_filtered_restore_read_write_data (dual, NULL, active_pfm);
}
dual->pfm->free_pfm (dual->pfm, active_pfm);
host_processor_filtered_set_host_flash_access (dual);
if (dual->reset_pulse) {
dual->control->hold_processor_in_reset (dual->control, true);
platform_msleep (dual->reset_pulse);
}
dual->control->hold_processor_in_reset (dual->control, false);
}
}
return status;
}
static int host_processor_dual_bypass_mode (struct host_processor *host, bool swap_flash)
{
struct host_processor_filtered *dual = (struct host_processor_filtered*) host;
if (dual == NULL) {
return HOST_PROCESSOR_INVALID_ARGUMENT;
}
platform_mutex_lock (&dual->lock);
host_processor_dual_force_bypass_mode (dual, swap_flash);
host_processor_filtered_set_host_flash_access (dual);
platform_mutex_unlock (&dual->lock);
return 0;
}
/**
* Configure the SPI filter to allow full read/write access to the host flash. This is effectively
* a bypass mode, but while blocking undesirable flash commands.
*
* @param host The host processor instance.
*
* @return 0 if the SPI filter was successfully configured or an error code.
*/
static int host_processor_dual_full_read_write_flash (struct host_processor_filtered *host)
{
struct flash_region rw;
struct pfm_read_write_regions writable;
int status;
rw.start_addr = 0;
rw.length = 0xffff0000;
writable.regions = &rw;
writable.count = 1;
status = host_fw_config_spi_filter_read_write_regions (host->filter, &writable);
if (status != 0) {
return status;
}
return host->filter->set_ro_cs (host->filter,
(host_state_manager_get_read_only_flash (host->state) == SPI_FILTER_CS_0) ?
SPI_FILTER_CS_1 : SPI_FILTER_CS_0);
}
/**
* Internal function to initialize the core components for host processor actions.
*
* @param host The host processor instance to initialize.
* @param control The interface for controlling the host processor.
* @param flash The manager for the flash devices for the host processor.
* @param state The state information for the host.
* @param filter The SPI filter controlling flash access for the host processor.
* @param pfm The manager for PFMs for the host processor.
* @param recovery The manager for recovery of the host processor.
* @param reset_pulse The length of the reset pulse to apply to the processor, in milliseconds. Set
* this to 0 to hold the processor instead of using a pulse.
* @param reset_flash The flag to indicate that host flash should be reset based on every
* host processor reset.
*
* @return 0 if the host processor interface was successfully initialized or an error code.
*/
int host_processor_dual_init_internal (struct host_processor_filtered *host,
const struct host_control *control, struct host_flash_manager_dual *flash,
struct host_state_manager *state, const struct spi_filter_interface *filter,
const struct pfm_manager *pfm, struct recovery_image_manager *recovery, int reset_pulse,
bool reset_flash)
{
int status;
if (host == NULL) {
return HOST_PROCESSOR_INVALID_ARGUMENT;
}
status = host_processor_filtered_init (host, control, &flash->base, state, filter, pfm,
recovery, reset_pulse, reset_flash);
if (status != 0) {
return status;
}
host->base.power_on_reset = host_processor_dual_power_on_reset;
host->base.soft_reset = host_processor_dual_soft_reset;
host->base.run_time_verification = host_processor_dual_run_time_verification;
host->base.flash_rollback = host_processor_dual_flash_rollback;
host->base.recover_active_read_write_data = host_processor_dual_recover_active_read_write_data;
host->base.get_next_reset_verification_actions =
host_processor_filtered_get_next_reset_verification_actions;
host->base.needs_config_recovery = host_processor_filtered_needs_config_recovery;
host->base.apply_recovery_image = host_processor_filtered_apply_recovery_image;
host->base.bypass_mode = host_processor_dual_bypass_mode;
host->internal.enable_bypass_mode = host_processor_dual_full_read_write_flash;
return 0;
}
/**
* Initialize the interface for executing host processor actions using two flash devices.
*
* @param host The host processor instance to initialize.
* @param control The interface for controlling the host processor.
* @param flash The manager for the flash devices for the host processor.
* @param state The state information for the host.
* @param filter The SPI filter controlling flash access for the host processor.
* @param pfm The manager for PFMs for the host processor.
* @param recovery The manager for recovery of the host processor.
*
* @return 0 if the host processor interface was successfully initialized or an error code.
*/
int host_processor_dual_init (struct host_processor_filtered *host,
const struct host_control *control, struct host_flash_manager_dual *flash,
struct host_state_manager *state, const struct spi_filter_interface *filter,
const struct pfm_manager *pfm, struct recovery_image_manager *recovery)
{
return host_processor_dual_init_internal (host, control, flash, state, filter, pfm, recovery, 0,
false);
}
/**
* Initialize the interface for executing host processor actions using two flash devices.
*
* While host flash is being accessed, the host processor will not be held in reset. After the host
* flash accesses have been completed, the host processor reset will be pulsed.
*
* @param host The host processor instance to initialize.
* @param control The interface for controlling the host processor.
* @param flash The manager for the flash devices for the host processor.
* @param state The state information for the host.
* @param filter The SPI filter controlling flash access for the host processor.
* @param pfm The manager for PFMs for the host processor.
* @param recovery The manager for recovery of the host processor.
* @param pulse_width The width of the reset pulse, in milliseconds.
*
* @return 0 if the host processor interface was successfully initialized or an error code.
*/
int host_processor_dual_init_pulse_reset (struct host_processor_filtered *host,
const struct host_control *control, struct host_flash_manager_dual *flash,
struct host_state_manager *state, const struct spi_filter_interface *filter,
const struct pfm_manager *pfm, struct recovery_image_manager *recovery, int pulse_width)
{
if (pulse_width <= 0) {
return HOST_PROCESSOR_INVALID_ARGUMENT;
}
return host_processor_dual_init_internal (host, control, flash, state, filter, pfm, recovery,
pulse_width, false);
}
/**
* Initialize the interface for executing host processor actions using two flash devices.
* The host flash device will reset on host resets.
*
* @param host The host processor instance to initialize.
* @param control The interface for controlling the host processor.
* @param flash The manager for the flash devices for the host processor.
* @param state The state information for the host.
* @param filter The SPI filter controlling flash access for the host processor.
* @param pfm The manager for PFMs for the host processor.
* @param recovery The manager for recovery of the host processor.
*
* @return 0 if the host processor interface was successfully initialized or an error code.
*/
int host_processor_dual_init_reset_flash (struct host_processor_filtered *host,
const struct host_control *control, struct host_flash_manager_dual *flash,
struct host_state_manager *state, const struct spi_filter_interface *filter,
const struct pfm_manager *pfm, struct recovery_image_manager *recovery)
{
return host_processor_dual_init_internal (host, control, flash, state, filter, pfm, recovery, 0,
true);
}
/**
* Initialize the interface for executing host processor actions using two flash devices.
*
* While host flash is being accessed, the host processor will not be held in reset. After the host
* flash accesses have been completed, the host processor reset will be pulsed.
*
* Host flash will reset on host resets.
*
* @param host The host processor instance to initialize.
* @param control The interface for controlling the host processor.
* @param flash The manager for the flash devices for the host processor.
* @param state The state information for the host.
* @param filter The SPI filter controlling flash access for the host processor.
* @param pfm The manager for PFMs for the host processor.
* @param recovery The manager for recovery of the host processor.
* @param pulse_width The width of the reset pulse, in milliseconds.
*
* @return 0 if the host processor interface was successfully initialized or an error code.
*/
int host_processor_dual_init_reset_flash_pulse_reset (struct host_processor_filtered *host,
const struct host_control *control, struct host_flash_manager_dual *flash,
struct host_state_manager *state, const struct spi_filter_interface *filter,
const struct pfm_manager *pfm, struct recovery_image_manager *recovery, int pulse_width)
{
if (pulse_width <= 0) {
return HOST_PROCESSOR_INVALID_ARGUMENT;
}
return host_processor_dual_init_internal (host, control, flash, state, filter, pfm, recovery,
pulse_width, true);
}
/**
* Release the resources used by the host processor interface.
*
* @param host The host processor instance to release.
*/
void host_processor_dual_release (struct host_processor_filtered *host)
{
host_processor_filtered_release (host);
}