| /* |
| * Copyright (c) 2022, Arm Limited. All rights reserved. |
| * |
| * SPDX-License-Identifier: BSD-3-Clause |
| * |
| */ |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <common/uuid/uuid.h> |
| #include <protocols/service/fwu/packed-c/status.h> |
| #include <protocols/service/fwu/packed-c/fwu_proto.h> |
| #include <service/fwu/fw_store/fw_store.h> |
| #include <service/fwu/inspector/fw_inspector.h> |
| #include "img_dir_serializer.h" |
| #include "update_agent.h" |
| |
| |
| static bool open_image_directory( |
| struct update_agent *update_agent, |
| const struct uuid_octets *uuid, |
| uint32_t *handle, |
| int *status); |
| |
| static bool open_fw_store_object( |
| struct update_agent *update_agent, |
| const struct uuid_octets *uuid, |
| uint32_t *handle, |
| int *status); |
| |
| static bool open_fw_image( |
| struct update_agent *update_agent, |
| const struct uuid_octets *uuid, |
| uint32_t *handle, |
| int *status); |
| |
| |
| int update_agent_init( |
| struct update_agent *update_agent, |
| unsigned int boot_index, |
| fw_inspector_inspect fw_inspect_method, |
| struct fw_store *fw_store) |
| { |
| assert(update_agent); |
| assert(fw_inspect_method); |
| assert(fw_store); |
| |
| int status = FWU_STATUS_UNKNOWN; |
| |
| update_agent->state = FWU_STATE_INITIALIZING; |
| update_agent->fw_inspect_method = fw_inspect_method; |
| update_agent->fw_store = fw_store; |
| update_agent->image_dir_buf_size = 0; |
| update_agent->image_dir_buf = NULL; |
| |
| stream_manager_init(&update_agent->stream_manager); |
| |
| /* Initialize and populate the fw_directory. The fw_inspector will |
| * obtain trustworthy information about the booted firmware and |
| * populate the fw_directory to reflect information about the booted |
| * firmware. |
| */ |
| fw_directory_init(&update_agent->fw_directory); |
| |
| status = update_agent->fw_inspect_method( |
| &update_agent->fw_directory, |
| boot_index); |
| if (status != FWU_STATUS_SUCCESS) |
| return status; |
| |
| /* Allow the associated fw_store to synchronize its state to the |
| * state of the booted firmware reflected by the fw_directory. |
| */ |
| status = fw_store_synchronize( |
| update_agent->fw_store, |
| &update_agent->fw_directory, |
| boot_index); |
| if (status != FWU_STATUS_SUCCESS) |
| return status; |
| |
| /* Allocate a buffer for holding the serialized image directory */ |
| update_agent->image_dir_buf_size = img_dir_serializer_get_len(&update_agent->fw_directory); |
| update_agent->image_dir_buf = malloc(update_agent->image_dir_buf_size); |
| if (!update_agent->image_dir_buf) |
| return FWU_STATUS_UNKNOWN; |
| |
| /* Transition to initial state */ |
| update_agent->state = |
| fw_store_is_trial(update_agent->fw_store) ? |
| FWU_STATE_TRIAL : FWU_STATE_REGULAR; |
| |
| return FWU_STATUS_SUCCESS; |
| } |
| |
| void update_agent_deinit( |
| struct update_agent *update_agent) |
| { |
| update_agent->state = FWU_STATE_DEINITIALZED; |
| |
| free(update_agent->image_dir_buf); |
| fw_directory_deinit(&update_agent->fw_directory); |
| stream_manager_deinit(&update_agent->stream_manager); |
| } |
| |
| int update_agent_begin_staging( |
| struct update_agent *update_agent) |
| { |
| int status = FWU_STATUS_DENIED; |
| |
| /* If already staging, any previous installation state is discarded */ |
| update_agent_cancel_staging(update_agent); |
| |
| if (update_agent->state == FWU_STATE_REGULAR) { |
| |
| status = fw_store_begin_install(update_agent->fw_store); |
| |
| /* Check if ready to install images */ |
| if (status == FWU_STATUS_SUCCESS) |
| update_agent->state = FWU_STATE_STAGING; |
| } |
| |
| return status; |
| } |
| |
| int update_agent_end_staging( |
| struct update_agent *update_agent) |
| { |
| int status = FWU_STATUS_DENIED; |
| |
| if (update_agent->state == FWU_STATE_STAGING) { |
| |
| /* The client is responsible for committing each installed image. If any |
| * install streams have been left open, not all images were committed. |
| */ |
| bool any_uncommitted = stream_manager_is_open_streams( |
| &update_agent->stream_manager, FWU_STREAM_TYPE_INSTALL); |
| |
| if (!any_uncommitted) { |
| |
| /* All installed images have been committed so we're |
| * ready for a trial. |
| */ |
| status = fw_store_finalize_install(update_agent->fw_store); |
| |
| if (status == FWU_STATUS_SUCCESS) |
| /* Transition to TRAIL_PENDING state. The trial actually starts |
| * when installed images are activated through a system restart. |
| */ |
| update_agent->state = FWU_STATE_TRIAL_PENDING; |
| |
| } else { |
| |
| /* Client failed to commit all images installed */ |
| status = FWU_STATUS_BUSY; |
| } |
| } |
| |
| return status; |
| } |
| |
| int update_agent_cancel_staging( |
| struct update_agent *update_agent) |
| { |
| int status = FWU_STATUS_DENIED; |
| |
| if (update_agent->state == FWU_STATE_STAGING) { |
| |
| stream_manager_cancel_streams( |
| &update_agent->stream_manager, FWU_STREAM_TYPE_INSTALL); |
| |
| fw_store_cancel_install(update_agent->fw_store); |
| |
| update_agent->state = FWU_STATE_REGULAR; |
| |
| status = FWU_STATUS_SUCCESS; |
| } |
| |
| return status; |
| } |
| |
| int update_agent_accept( |
| struct update_agent *update_agent, |
| const struct uuid_octets *image_type_uuid) |
| { |
| int status = FWU_STATUS_DENIED; |
| |
| if (update_agent->state == FWU_STATE_TRIAL) { |
| |
| const struct image_info *image_info = fw_directory_find_image_info( |
| &update_agent->fw_directory, |
| image_type_uuid); |
| |
| if (image_info) { |
| |
| if (fw_store_notify_accepted( |
| update_agent->fw_store, image_info)) { |
| |
| /* From the fw_store perspective, the update has |
| * been fully accepted. |
| */ |
| status = fw_store_commit_to_update(update_agent->fw_store); |
| update_agent->state = FWU_STATE_REGULAR; |
| |
| } else |
| /* Still more images to accept */ |
| status = FWU_STATUS_SUCCESS; |
| } else |
| /* Unrecognised image uuid */ |
| status = FWU_STATUS_UNKNOWN; |
| } |
| |
| return status; |
| } |
| |
| int update_agent_select_previous( |
| struct update_agent *update_agent) |
| { |
| int status = FWU_STATUS_DENIED; |
| |
| if ((update_agent->state == FWU_STATE_TRIAL) || |
| (update_agent->state == FWU_STATE_TRIAL_PENDING)) { |
| |
| status = fw_store_revert_to_previous(update_agent->fw_store); |
| update_agent->state = FWU_STATE_REGULAR; |
| } |
| |
| return status; |
| } |
| |
| int update_agent_open( |
| struct update_agent *update_agent, |
| const struct uuid_octets *uuid, |
| uint32_t *handle) |
| { |
| int status; |
| |
| /* Pass UUID along a chain-of-responsibility until it's handled */ |
| if (!open_image_directory(update_agent, uuid, handle, &status) && |
| !open_fw_store_object(update_agent, uuid, handle, &status) && |
| !open_fw_image(update_agent, uuid, handle, &status)) { |
| |
| /* UUID not recognised */ |
| status = FWU_STATUS_UNKNOWN; |
| } |
| |
| return status; |
| } |
| |
| int update_agent_commit( |
| struct update_agent *update_agent, |
| uint32_t handle, |
| bool accepted) |
| { |
| return stream_manager_close( |
| &update_agent->stream_manager, |
| handle, |
| accepted); |
| } |
| |
| int update_agent_write_stream( |
| struct update_agent *update_agent, |
| uint32_t handle, |
| const uint8_t *data, |
| size_t data_len) |
| { |
| return stream_manager_write( |
| &update_agent->stream_manager, |
| handle, |
| data, data_len); |
| } |
| |
| int update_agent_read_stream( |
| struct update_agent *update_agent, |
| uint32_t handle, |
| uint8_t *buf, |
| size_t buf_size, |
| size_t *read_len, |
| size_t *total_len) |
| { |
| return stream_manager_read( |
| &update_agent->stream_manager, |
| handle, |
| buf, buf_size, |
| read_len, total_len); |
| } |
| |
| static bool open_image_directory( |
| struct update_agent *update_agent, |
| const struct uuid_octets *uuid, |
| uint32_t *handle, |
| int *status) |
| { |
| struct uuid_octets target_uuid; |
| |
| uuid_guid_octets_from_canonical(&target_uuid, FWU_DIRECTORY_CANONICAL_UUID); |
| |
| if (uuid_is_equal(uuid->octets, target_uuid.octets)) { |
| |
| /* Serialize a fresh view of the image directory */ |
| size_t serialized_len = 0; |
| |
| *status = img_dir_serializer_serialize( |
| &update_agent->fw_directory, |
| update_agent->fw_store, |
| update_agent->image_dir_buf, |
| update_agent->image_dir_buf_size, |
| &serialized_len); |
| |
| if (*status == FWU_STATUS_SUCCESS) { |
| |
| *status = stream_manager_open_buffer_stream( |
| &update_agent->stream_manager, |
| update_agent->image_dir_buf, |
| serialized_len, |
| handle); |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool open_fw_store_object( |
| struct update_agent *update_agent, |
| const struct uuid_octets *uuid, |
| uint32_t *handle, |
| int *status) |
| { |
| const uint8_t *exported_data; |
| size_t exported_data_len; |
| |
| if (fw_store_export( |
| update_agent->fw_store, |
| uuid, |
| &exported_data, &exported_data_len, |
| status)) { |
| |
| if (*status == FWU_STATUS_SUCCESS) { |
| |
| *status = stream_manager_open_buffer_stream( |
| &update_agent->stream_manager, |
| exported_data, |
| exported_data_len, |
| handle); |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |
| |
| static bool open_fw_image( |
| struct update_agent *update_agent, |
| const struct uuid_octets *uuid, |
| uint32_t *handle, |
| int *status) |
| { |
| const struct image_info *image_info = fw_directory_find_image_info( |
| &update_agent->fw_directory, |
| uuid); |
| |
| if (image_info) { |
| |
| if (update_agent->state == FWU_STATE_STAGING) { |
| |
| struct installer *installer; |
| |
| *status = fw_store_select_installer( |
| update_agent->fw_store, |
| image_info, |
| &installer); |
| |
| if (*status == FWU_STATUS_SUCCESS) { |
| |
| *status = stream_manager_open_install_stream( |
| &update_agent->stream_manager, |
| update_agent->fw_store, |
| installer, |
| image_info, |
| handle); |
| } |
| } else { |
| |
| /* Attempting to open a fw image when not staging */ |
| *status = FWU_STATUS_DENIED; |
| } |
| |
| return true; |
| } |
| |
| return false; |
| } |