Julian Hall | e35efa5 | 2022-10-31 16:34:58 +0000 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright (c) 2022, Arm Limited. All rights reserved. |
| 3 | * |
| 4 | * SPDX-License-Identifier: BSD-3-Clause |
| 5 | * |
| 6 | */ |
| 7 | |
| 8 | #include <assert.h> |
| 9 | #include <stdlib.h> |
| 10 | #include <string.h> |
| 11 | #include <common/uuid/uuid.h> |
| 12 | #include <protocols/service/fwu/packed-c/status.h> |
| 13 | #include <protocols/service/fwu/packed-c/fwu_proto.h> |
| 14 | #include <service/fwu/fw_store/fw_store.h> |
| 15 | #include <service/fwu/inspector/fw_inspector.h> |
| 16 | #include "img_dir_serializer.h" |
| 17 | #include "update_agent.h" |
| 18 | |
| 19 | |
| 20 | static bool open_image_directory( |
| 21 | struct update_agent *update_agent, |
| 22 | const struct uuid_octets *uuid, |
| 23 | uint32_t *handle, |
| 24 | int *status); |
| 25 | |
| 26 | static bool open_fw_store_object( |
| 27 | struct update_agent *update_agent, |
| 28 | const struct uuid_octets *uuid, |
| 29 | uint32_t *handle, |
| 30 | int *status); |
| 31 | |
| 32 | static bool open_fw_image( |
| 33 | struct update_agent *update_agent, |
| 34 | const struct uuid_octets *uuid, |
| 35 | uint32_t *handle, |
| 36 | int *status); |
| 37 | |
| 38 | |
| 39 | int update_agent_init( |
| 40 | struct update_agent *update_agent, |
| 41 | unsigned int boot_index, |
| 42 | fw_inspector_inspect fw_inspect_method, |
| 43 | struct fw_store *fw_store) |
| 44 | { |
| 45 | assert(update_agent); |
| 46 | assert(fw_inspect_method); |
| 47 | assert(fw_store); |
| 48 | |
| 49 | int status = FWU_STATUS_UNKNOWN; |
| 50 | |
| 51 | update_agent->state = FWU_STATE_INITIALIZING; |
| 52 | update_agent->fw_inspect_method = fw_inspect_method; |
| 53 | update_agent->fw_store = fw_store; |
| 54 | update_agent->image_dir_buf_size = 0; |
| 55 | update_agent->image_dir_buf = NULL; |
| 56 | |
| 57 | stream_manager_init(&update_agent->stream_manager); |
| 58 | |
| 59 | /* Initialize and populate the fw_directory. The fw_inspector will |
| 60 | * obtain trustworthy information about the booted firmware and |
| 61 | * populate the fw_directory to reflect information about the booted |
| 62 | * firmware. |
| 63 | */ |
| 64 | fw_directory_init(&update_agent->fw_directory); |
| 65 | |
| 66 | status = update_agent->fw_inspect_method( |
| 67 | &update_agent->fw_directory, |
| 68 | boot_index); |
| 69 | if (status != FWU_STATUS_SUCCESS) |
| 70 | return status; |
| 71 | |
| 72 | /* Allow the associated fw_store to synchronize its state to the |
| 73 | * state of the booted firmware reflected by the fw_directory. |
| 74 | */ |
| 75 | status = fw_store_synchronize( |
| 76 | update_agent->fw_store, |
| 77 | &update_agent->fw_directory, |
| 78 | boot_index); |
| 79 | if (status != FWU_STATUS_SUCCESS) |
| 80 | return status; |
| 81 | |
| 82 | /* Allocate a buffer for holding the serialized image directory */ |
| 83 | update_agent->image_dir_buf_size = img_dir_serializer_get_len(&update_agent->fw_directory); |
| 84 | update_agent->image_dir_buf = malloc(update_agent->image_dir_buf_size); |
| 85 | if (!update_agent->image_dir_buf) |
| 86 | return FWU_STATUS_UNKNOWN; |
| 87 | |
| 88 | /* Transition to initial state */ |
| 89 | update_agent->state = |
| 90 | fw_store_is_trial(update_agent->fw_store) ? |
| 91 | FWU_STATE_TRIAL : FWU_STATE_REGULAR; |
| 92 | |
| 93 | return FWU_STATUS_SUCCESS; |
| 94 | } |
| 95 | |
| 96 | void update_agent_deinit( |
| 97 | struct update_agent *update_agent) |
| 98 | { |
| 99 | update_agent->state = FWU_STATE_DEINITIALZED; |
| 100 | |
| 101 | free(update_agent->image_dir_buf); |
| 102 | fw_directory_deinit(&update_agent->fw_directory); |
| 103 | stream_manager_deinit(&update_agent->stream_manager); |
| 104 | } |
| 105 | |
| 106 | int update_agent_begin_staging( |
| 107 | struct update_agent *update_agent) |
| 108 | { |
| 109 | int status = FWU_STATUS_DENIED; |
| 110 | |
| 111 | /* If already staging, any previous installation state is discarded */ |
| 112 | update_agent_cancel_staging(update_agent); |
| 113 | |
| 114 | if (update_agent->state == FWU_STATE_REGULAR) { |
| 115 | |
| 116 | status = fw_store_begin_install(update_agent->fw_store); |
| 117 | |
| 118 | /* Check if ready to install images */ |
| 119 | if (status == FWU_STATUS_SUCCESS) |
| 120 | update_agent->state = FWU_STATE_STAGING; |
| 121 | } |
| 122 | |
| 123 | return status; |
| 124 | } |
| 125 | |
| 126 | int update_agent_end_staging( |
| 127 | struct update_agent *update_agent) |
| 128 | { |
| 129 | int status = FWU_STATUS_DENIED; |
| 130 | |
| 131 | if (update_agent->state == FWU_STATE_STAGING) { |
| 132 | |
| 133 | /* The client is responsible for committing each installed image. If any |
| 134 | * install streams have been left open, not all images were committed. |
| 135 | */ |
| 136 | bool any_uncommitted = stream_manager_is_open_streams( |
| 137 | &update_agent->stream_manager, FWU_STREAM_TYPE_INSTALL); |
| 138 | |
| 139 | if (!any_uncommitted) { |
| 140 | |
| 141 | /* All installed images have been committed so we're |
| 142 | * ready for a trial. |
| 143 | */ |
| 144 | status = fw_store_finalize_install(update_agent->fw_store); |
| 145 | |
| 146 | if (status == FWU_STATUS_SUCCESS) |
| 147 | /* Transition to TRAIL_PENDING state. The trial actually starts |
| 148 | * when installed images are activated through a system restart. |
| 149 | */ |
| 150 | update_agent->state = FWU_STATE_TRIAL_PENDING; |
| 151 | |
| 152 | } else { |
| 153 | |
| 154 | /* Client failed to commit all images installed */ |
| 155 | status = FWU_STATUS_BUSY; |
| 156 | } |
| 157 | } |
| 158 | |
| 159 | return status; |
| 160 | } |
| 161 | |
| 162 | int update_agent_cancel_staging( |
| 163 | struct update_agent *update_agent) |
| 164 | { |
| 165 | int status = FWU_STATUS_DENIED; |
| 166 | |
| 167 | if (update_agent->state == FWU_STATE_STAGING) { |
| 168 | |
| 169 | stream_manager_cancel_streams( |
| 170 | &update_agent->stream_manager, FWU_STREAM_TYPE_INSTALL); |
| 171 | |
| 172 | fw_store_cancel_install(update_agent->fw_store); |
| 173 | |
| 174 | update_agent->state = FWU_STATE_REGULAR; |
| 175 | |
| 176 | status = FWU_STATUS_SUCCESS; |
| 177 | } |
| 178 | |
| 179 | return status; |
| 180 | } |
| 181 | |
| 182 | int update_agent_accept( |
| 183 | struct update_agent *update_agent, |
| 184 | const struct uuid_octets *image_type_uuid) |
| 185 | { |
| 186 | int status = FWU_STATUS_DENIED; |
| 187 | |
| 188 | if (update_agent->state == FWU_STATE_TRIAL) { |
| 189 | |
| 190 | const struct image_info *image_info = fw_directory_find_image_info( |
| 191 | &update_agent->fw_directory, |
| 192 | image_type_uuid); |
| 193 | |
| 194 | if (image_info) { |
| 195 | |
| 196 | if (fw_store_notify_accepted( |
| 197 | update_agent->fw_store, image_info)) { |
| 198 | |
| 199 | /* From the fw_store perspective, the update has |
| 200 | * been fully accepted. |
| 201 | */ |
| 202 | status = fw_store_commit_to_update(update_agent->fw_store); |
| 203 | update_agent->state = FWU_STATE_REGULAR; |
| 204 | |
| 205 | } else |
| 206 | /* Still more images to accept */ |
| 207 | status = FWU_STATUS_SUCCESS; |
| 208 | } else |
| 209 | /* Unrecognised image uuid */ |
| 210 | status = FWU_STATUS_UNKNOWN; |
| 211 | } |
| 212 | |
| 213 | return status; |
| 214 | } |
| 215 | |
| 216 | int update_agent_select_previous( |
| 217 | struct update_agent *update_agent) |
| 218 | { |
| 219 | int status = FWU_STATUS_DENIED; |
| 220 | |
| 221 | if ((update_agent->state == FWU_STATE_TRIAL) || |
| 222 | (update_agent->state == FWU_STATE_TRIAL_PENDING)) { |
| 223 | |
| 224 | status = fw_store_revert_to_previous(update_agent->fw_store); |
| 225 | update_agent->state = FWU_STATE_REGULAR; |
| 226 | } |
| 227 | |
| 228 | return status; |
| 229 | } |
| 230 | |
| 231 | int update_agent_open( |
| 232 | struct update_agent *update_agent, |
| 233 | const struct uuid_octets *uuid, |
| 234 | uint32_t *handle) |
| 235 | { |
| 236 | int status; |
| 237 | |
| 238 | /* Pass UUID along a chain-of-responsibility until it's handled */ |
| 239 | if (!open_image_directory(update_agent, uuid, handle, &status) && |
| 240 | !open_fw_store_object(update_agent, uuid, handle, &status) && |
| 241 | !open_fw_image(update_agent, uuid, handle, &status)) { |
| 242 | |
| 243 | /* UUID not recognised */ |
| 244 | status = FWU_STATUS_UNKNOWN; |
| 245 | } |
| 246 | |
| 247 | return status; |
| 248 | } |
| 249 | |
| 250 | int update_agent_commit( |
| 251 | struct update_agent *update_agent, |
| 252 | uint32_t handle, |
| 253 | bool accepted) |
| 254 | { |
| 255 | return stream_manager_close( |
| 256 | &update_agent->stream_manager, |
| 257 | handle, |
| 258 | accepted); |
| 259 | } |
| 260 | |
| 261 | int update_agent_write_stream( |
| 262 | struct update_agent *update_agent, |
| 263 | uint32_t handle, |
| 264 | const uint8_t *data, |
| 265 | size_t data_len) |
| 266 | { |
| 267 | return stream_manager_write( |
| 268 | &update_agent->stream_manager, |
| 269 | handle, |
| 270 | data, data_len); |
| 271 | } |
| 272 | |
| 273 | int update_agent_read_stream( |
| 274 | struct update_agent *update_agent, |
| 275 | uint32_t handle, |
| 276 | uint8_t *buf, |
| 277 | size_t buf_size, |
| 278 | size_t *read_len, |
| 279 | size_t *total_len) |
| 280 | { |
| 281 | return stream_manager_read( |
| 282 | &update_agent->stream_manager, |
| 283 | handle, |
| 284 | buf, buf_size, |
| 285 | read_len, total_len); |
| 286 | } |
| 287 | |
| 288 | static bool open_image_directory( |
| 289 | struct update_agent *update_agent, |
| 290 | const struct uuid_octets *uuid, |
| 291 | uint32_t *handle, |
| 292 | int *status) |
| 293 | { |
| 294 | struct uuid_octets target_uuid; |
| 295 | |
| 296 | uuid_guid_octets_from_canonical(&target_uuid, FWU_DIRECTORY_CANONICAL_UUID); |
| 297 | |
| 298 | if (uuid_is_equal(uuid->octets, target_uuid.octets)) { |
| 299 | |
| 300 | /* Serialize a fresh view of the image directory */ |
| 301 | size_t serialized_len = 0; |
| 302 | |
| 303 | *status = img_dir_serializer_serialize( |
| 304 | &update_agent->fw_directory, |
| 305 | update_agent->fw_store, |
| 306 | update_agent->image_dir_buf, |
| 307 | update_agent->image_dir_buf_size, |
| 308 | &serialized_len); |
| 309 | |
| 310 | if (*status == FWU_STATUS_SUCCESS) { |
| 311 | |
| 312 | *status = stream_manager_open_buffer_stream( |
| 313 | &update_agent->stream_manager, |
| 314 | update_agent->image_dir_buf, |
| 315 | serialized_len, |
| 316 | handle); |
| 317 | } |
| 318 | |
| 319 | return true; |
| 320 | } |
| 321 | |
| 322 | return false; |
| 323 | } |
| 324 | |
| 325 | static bool open_fw_store_object( |
| 326 | struct update_agent *update_agent, |
| 327 | const struct uuid_octets *uuid, |
| 328 | uint32_t *handle, |
| 329 | int *status) |
| 330 | { |
| 331 | const uint8_t *exported_data; |
| 332 | size_t exported_data_len; |
| 333 | |
| 334 | if (fw_store_export( |
| 335 | update_agent->fw_store, |
| 336 | uuid, |
| 337 | &exported_data, &exported_data_len, |
| 338 | status)) { |
| 339 | |
| 340 | if (*status == FWU_STATUS_SUCCESS) { |
| 341 | |
| 342 | *status = stream_manager_open_buffer_stream( |
| 343 | &update_agent->stream_manager, |
| 344 | exported_data, |
| 345 | exported_data_len, |
| 346 | handle); |
| 347 | } |
| 348 | |
| 349 | return true; |
| 350 | } |
| 351 | |
| 352 | return false; |
| 353 | } |
| 354 | |
| 355 | static bool open_fw_image( |
| 356 | struct update_agent *update_agent, |
| 357 | const struct uuid_octets *uuid, |
| 358 | uint32_t *handle, |
| 359 | int *status) |
| 360 | { |
| 361 | const struct image_info *image_info = fw_directory_find_image_info( |
| 362 | &update_agent->fw_directory, |
| 363 | uuid); |
| 364 | |
| 365 | if (image_info) { |
| 366 | |
| 367 | if (update_agent->state == FWU_STATE_STAGING) { |
| 368 | |
| 369 | struct installer *installer; |
| 370 | |
| 371 | *status = fw_store_select_installer( |
| 372 | update_agent->fw_store, |
| 373 | image_info, |
| 374 | &installer); |
| 375 | |
| 376 | if (*status == FWU_STATUS_SUCCESS) { |
| 377 | |
| 378 | *status = stream_manager_open_install_stream( |
| 379 | &update_agent->stream_manager, |
| 380 | update_agent->fw_store, |
| 381 | installer, |
| 382 | image_info, |
| 383 | handle); |
| 384 | } |
| 385 | } else { |
| 386 | |
| 387 | /* Attempting to open a fw image when not staging */ |
| 388 | *status = FWU_STATUS_DENIED; |
| 389 | } |
| 390 | |
| 391 | return true; |
| 392 | } |
| 393 | |
| 394 | return false; |
| 395 | } |