core/firmware/impactful_update.c (132 lines of code) (raw):
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "firmware_logging.h"
#include "impactful_update.h"
#include "common/unused.h"
int impactful_update_is_update_not_impactful (const struct impactful_update_interface *impactful)
{
const struct impactful_update *update = (const struct impactful_update*) impactful;
size_t i;
int status;
if (update == NULL) {
return IMPACTFUL_UPDATE_INVALID_ARGUMENT;
}
for (i = 0; i < update->check_count; i++) {
status = update->check[i]->is_not_impactful (update->check[i]);
if (status != 0) {
break;
}
}
return status;
}
int impactful_update_is_update_allowed (const struct impactful_update_interface *impactful)
{
const struct impactful_update *update = (const struct impactful_update*) impactful;
size_t i;
int status;
int allowed = 0;
if (update == NULL) {
return IMPACTFUL_UPDATE_INVALID_ARGUMENT;
}
for (i = 0; i < update->check_count; i++) {
status = update->check[i]->is_not_impactful (update->check[i]);
if (status != 0) {
int auth_allowed;
/* The check has determined the update will be impactful. Check to see if the update
* can be authorized or not. */
auth_allowed = update->check[i]->is_authorization_allowed (update->check[i]);
if (auth_allowed != 0) {
/* Impactful updates cannot be authorized for this check, so no need to make any
* additional checks. */
debug_log_create_entry (DEBUG_LOG_SEVERITY_WARNING, DEBUG_LOG_COMPONENT_CERBERUS_FW,
FIRMWARE_LOGGING_IMPACTFUL_BLOCKED, i, auth_allowed);
return auth_allowed;
}
platform_mutex_lock (&update->state->lock);
if (!update->state->is_authorized) {
debug_log_create_entry (DEBUG_LOG_SEVERITY_INFO, DEBUG_LOG_COMPONENT_CERBERUS_FW,
FIRMWARE_LOGGING_IMPACTFUL_UPDATE_NO_AUTH, i, status);
allowed = status;
}
platform_mutex_unlock (&update->state->lock);
/* The rest of the checks must be performed to ensure there are no blocking impactful
* conditions. */
}
}
return allowed;
}
int impactful_update_authorize_update (const struct impactful_update_interface *impactful,
uint32_t allowed_time_ms)
{
const struct impactful_update *update = (const struct impactful_update*) impactful;
int status;
if (update == NULL) {
return IMPACTFUL_UPDATE_INVALID_ARGUMENT;
}
platform_mutex_lock (&update->state->lock);
if (allowed_time_ms != 0) {
status = platform_timer_arm_one_shot (&update->state->expiration, allowed_time_ms);
}
else {
/* No expiration is requested, so cancel any existing expiration timer. */
status = platform_timer_disarm (&update->state->expiration);
}
if (status == 0) {
/* Only if the expiration timer could be set appropriately will impactful updates be
* authorized. */
update->state->is_authorized = true;
}
platform_mutex_unlock (&update->state->lock);
return status;
}
int impactful_update_reset_authorization (const struct impactful_update_interface *impactful)
{
const struct impactful_update *update = (const struct impactful_update*) impactful;
if (update == NULL) {
return IMPACTFUL_UPDATE_INVALID_ARGUMENT;
}
/* This is just for efficiency so the timer doesn't fire unnecessarily, so we don't care about
* the status of this call. If the timer still ends of triggering, it would not change any
* state. */
platform_timer_disarm (&update->state->expiration);
platform_mutex_lock (&update->state->lock);
update->state->is_authorized = false;
platform_mutex_unlock (&update->state->lock);
return 0;
}
/**
* Timer callback to force expiration of any active impactful update authorization.
*
* @param impactful The impactful update context to update.
*/
static void impactful_update_expired_authorization (
const struct impactful_update_interface *impactful)
{
impactful_update_reset_authorization (impactful);
}
/**
* Initialize a manager for providing the special handling needed to work with firmware updates that
* will be impactful to the host system.
*
* @param impactful The impactful update manager to initialize.
* @param state Variable context for the manager. This must be uninitialized.
* @param check List of checks to perform when determining whether an update is impactful or not.
* @param check_count The number of impactful checks in the list.
*
* @return 0 if the impactful update manager was initialized successfully or an error code.
*/
int impactful_update_init (struct impactful_update *impactful, struct impactful_update_state *state,
const struct impactful_check *const *check, size_t check_count)
{
if ((impactful == NULL) || (state == NULL) || (check == NULL) || (check_count == 0)) {
return IMPACTFUL_UPDATE_INVALID_ARGUMENT;
}
memset (impactful, 0, sizeof (*impactful));
impactful->base.is_update_not_impactful = impactful_update_is_update_not_impactful;
impactful->base.is_update_allowed = impactful_update_is_update_allowed;
impactful->base.authorize_update = impactful_update_authorize_update;
impactful->base.reset_authorization = impactful_update_reset_authorization;
impactful->state = state;
impactful->check = check;
impactful->check_count = check_count;
return impactful_update_init_state (impactful);
}
/**
* Initialize only the variable state for an impactful update manager. The rest of the manager is
* assumed to have already been initialized.
*
* This would generally be used with a statically initialized instance.
*
* @param impactful The impactful update manager that contains the state to initialize.
*
* @return 0 if the state was successfully initialized or an error code.
*/
int impactful_update_init_state (const struct impactful_update *impactful)
{
int status;
if ((impactful == NULL) || (impactful->state == NULL) || (impactful->check == NULL) ||
(impactful->check_count == 0)) {
return IMPACTFUL_UPDATE_INVALID_ARGUMENT;
}
memset (impactful->state, 0, sizeof (*impactful->state));
status = platform_mutex_init (&impactful->state->lock);
if (status != 0) {
return status;
}
status = platform_timer_create (&impactful->state->expiration,
(timer_callback) impactful_update_expired_authorization, (void*) &impactful->base);
if (status != 0) {
platform_mutex_free (&impactful->state->lock);
}
return status;
}
/**
* Release the resources used for managing impactful firmware updates.
*
* @param impactful The impactful update manager to release.
*/
void impactful_update_release (const struct impactful_update *impactful)
{
if (impactful != NULL) {
platform_timer_delete (&impactful->state->expiration);
platform_mutex_free (&impactful->state->lock);
}
}