blob: 733a012af435aa5f94c47bb7a52ebfcc54e6b45e [file] [log] [blame]
Julian Halle35efa52022-10-31 16:34:58 +00001/*
Imre Kisefadd3a2024-06-28 14:15:57 +02002 * Copyright (c) 2022-2024, Arm Limited. All rights reserved.
Julian Halle35efa52022-10-31 16:34:58 +00003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 *
6 */
7
Gyorgy Szing3c446242023-03-31 01:53:15 +02008#include "update_agent.h"
9
Julian Halle35efa52022-10-31 16:34:58 +000010#include <assert.h>
11#include <stdlib.h>
12#include <string.h>
Gyorgy Szing3c446242023-03-31 01:53:15 +020013
14#include "common/uuid/uuid.h"
Julian Halle35efa52022-10-31 16:34:58 +000015#include "img_dir_serializer.h"
Gyorgy Szing3c446242023-03-31 01:53:15 +020016#include "protocols/service/fwu/packed-c/fwu_proto.h"
17#include "protocols/service/fwu/packed-c/status.h"
18#include "service/fwu/fw_store/fw_store.h"
19#include "service/fwu/inspector/fw_inspector.h"
Imre Kisefadd3a2024-06-28 14:15:57 +020020#include "trace.h"
Julian Halle35efa52022-10-31 16:34:58 +000021
Imre Kisefadd3a2024-06-28 14:15:57 +020022/**
23 * \brief Update process states
24 *
25 * The update_agent is responsible for ensuring that only a valid update flow
26 * is followed by a client. To enforce the flow, public operations can only be
27 * used in a valid state that reflects the FWU-A behavioral model.
28 */
29enum fwu_state {
30 FWU_STATE_DEINITIALZED,
31 FWU_STATE_INITIALIZING,
32 FWU_STATE_REGULAR,
33 FWU_STATE_STAGING,
34 FWU_STATE_TRIAL_PENDING,
35 FWU_STATE_TRIAL
36};
Julian Halle35efa52022-10-31 16:34:58 +000037
Imre Kisefadd3a2024-06-28 14:15:57 +020038/**
39 * \brief update_agent structure definition
40 *
41 * An update_agent instance is responsible for coordinating firmware updates applied
42 * to a fw_store. An update_agent performs a security role by enforcing that a
43 * valid flow is performed to update the fw store.
44 */
45struct generic_update_agent {
46 enum fwu_state state;
47 fw_inspector_inspect fw_inspect_method;
48 struct fw_store *fw_store;
49 struct fw_directory fw_directory;
50 struct stream_manager stream_manager;
51 uint8_t *image_dir_buf;
52 size_t image_dir_buf_size;
53};
Julian Halle35efa52022-10-31 16:34:58 +000054
Imre Kisefadd3a2024-06-28 14:15:57 +020055static int cancel_staging(void *context);
Julian Halle35efa52022-10-31 16:34:58 +000056
Imre Kis18b387d2024-07-02 13:51:07 +020057static int discover(void *context, struct fwu_discovery_result *result)
58{
59 result->service_status = 0;
60 result->version_major = FWU_PROTOCOL_VERSION_MAJOR;
61 result->version_minor = FWU_PROTOCOL_VERSION_MINOR;
62 result->max_payload_size = 0;
63 result->flags = 0;
64 result->vendor_specific_flags = 0;
65
66 return FWU_STATUS_SUCCESS;
67}
68
Imre Kisefadd3a2024-06-28 14:15:57 +020069static int begin_staging(void *context, uint32_t vendor_flags, uint32_t partial_update_count,
70 const struct uuid_octets *update_guid)
Julian Halle35efa52022-10-31 16:34:58 +000071{
72 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +020073 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +000074
75 /* If already staging, any previous installation state is discarded */
Imre Kisefadd3a2024-06-28 14:15:57 +020076 cancel_staging(update_agent);
Julian Halle35efa52022-10-31 16:34:58 +000077
78 if (update_agent->state == FWU_STATE_REGULAR) {
Julian Halle35efa52022-10-31 16:34:58 +000079 status = fw_store_begin_install(update_agent->fw_store);
80
81 /* Check if ready to install images */
82 if (status == FWU_STATUS_SUCCESS)
83 update_agent->state = FWU_STATE_STAGING;
84 }
85
86 return status;
87}
88
Imre Kisefadd3a2024-06-28 14:15:57 +020089static int end_staging(void *context)
Julian Halle35efa52022-10-31 16:34:58 +000090{
91 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +020092 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +000093
94 if (update_agent->state == FWU_STATE_STAGING) {
Julian Halle35efa52022-10-31 16:34:58 +000095 /* The client is responsible for committing each installed image. If any
96 * install streams have been left open, not all images were committed.
97 */
Gyorgy Szing3c446242023-03-31 01:53:15 +020098 bool any_uncommitted = stream_manager_is_open_streams(&update_agent->stream_manager,
99 FWU_STREAM_TYPE_INSTALL);
Julian Halle35efa52022-10-31 16:34:58 +0000100
101 if (!any_uncommitted) {
Julian Halle35efa52022-10-31 16:34:58 +0000102 /* All installed images have been committed so we're
103 * ready for a trial.
104 */
105 status = fw_store_finalize_install(update_agent->fw_store);
106
107 if (status == FWU_STATUS_SUCCESS)
108 /* Transition to TRAIL_PENDING state. The trial actually starts
109 * when installed images are activated through a system restart.
110 */
111 update_agent->state = FWU_STATE_TRIAL_PENDING;
112
113 } else {
Julian Halle35efa52022-10-31 16:34:58 +0000114 /* Client failed to commit all images installed */
115 status = FWU_STATUS_BUSY;
116 }
117 }
118
119 return status;
120}
121
Imre Kisefadd3a2024-06-28 14:15:57 +0200122static int cancel_staging(void *context)
Julian Halle35efa52022-10-31 16:34:58 +0000123{
124 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +0200125 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +0000126
127 if (update_agent->state == FWU_STATE_STAGING) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200128 stream_manager_cancel_streams(&update_agent->stream_manager,
129 FWU_STREAM_TYPE_INSTALL);
Julian Halle35efa52022-10-31 16:34:58 +0000130
131 fw_store_cancel_install(update_agent->fw_store);
132
133 update_agent->state = FWU_STATE_REGULAR;
134
135 status = FWU_STATUS_SUCCESS;
136 }
137
138 return status;
139}
140
Imre Kisefadd3a2024-06-28 14:15:57 +0200141static int accept(void *context, const struct uuid_octets *image_type_uuid)
Julian Halle35efa52022-10-31 16:34:58 +0000142{
143 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +0200144 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +0000145
146 if (update_agent->state == FWU_STATE_TRIAL) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200147 const struct image_info *image_info =
148 fw_directory_find_image_info(&update_agent->fw_directory, image_type_uuid);
Julian Halle35efa52022-10-31 16:34:58 +0000149
150 if (image_info) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200151 if (fw_store_notify_accepted(update_agent->fw_store, image_info)) {
Julian Halle35efa52022-10-31 16:34:58 +0000152 /* From the fw_store perspective, the update has
153 * been fully accepted.
154 */
155 status = fw_store_commit_to_update(update_agent->fw_store);
156 update_agent->state = FWU_STATE_REGULAR;
157
158 } else
159 /* Still more images to accept */
160 status = FWU_STATUS_SUCCESS;
161 } else
162 /* Unrecognised image uuid */
163 status = FWU_STATUS_UNKNOWN;
164 }
165
166 return status;
167}
168
Imre Kisefadd3a2024-06-28 14:15:57 +0200169static int select_previous(void *context)
Julian Halle35efa52022-10-31 16:34:58 +0000170{
171 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +0200172 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +0000173
174 if ((update_agent->state == FWU_STATE_TRIAL) ||
Gyorgy Szing3c446242023-03-31 01:53:15 +0200175 (update_agent->state == FWU_STATE_TRIAL_PENDING)) {
Julian Halle35efa52022-10-31 16:34:58 +0000176 status = fw_store_revert_to_previous(update_agent->fw_store);
177 update_agent->state = FWU_STATE_REGULAR;
178 }
179
180 return status;
181}
182
Imre Kisefadd3a2024-06-28 14:15:57 +0200183static bool open_image_directory(struct generic_update_agent *update_agent,
184 const struct uuid_octets *uuid, uint32_t *handle, int *status)
Julian Halle35efa52022-10-31 16:34:58 +0000185{
186 struct uuid_octets target_uuid;
187
188 uuid_guid_octets_from_canonical(&target_uuid, FWU_DIRECTORY_CANONICAL_UUID);
189
190 if (uuid_is_equal(uuid->octets, target_uuid.octets)) {
Julian Halle35efa52022-10-31 16:34:58 +0000191 /* Serialize a fresh view of the image directory */
192 size_t serialized_len = 0;
193
Gyorgy Szing3c446242023-03-31 01:53:15 +0200194 *status = img_dir_serializer_serialize(&update_agent->fw_directory,
195 update_agent->fw_store,
196 update_agent->image_dir_buf,
197 update_agent->image_dir_buf_size,
198 &serialized_len);
Julian Halle35efa52022-10-31 16:34:58 +0000199
200 if (*status == FWU_STATUS_SUCCESS) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200201 *status = stream_manager_open_buffer_stream(&update_agent->stream_manager,
202 update_agent->image_dir_buf,
203 serialized_len, handle);
Julian Halle35efa52022-10-31 16:34:58 +0000204 }
205
206 return true;
207 }
208
209 return false;
210}
211
Imre Kisefadd3a2024-06-28 14:15:57 +0200212static bool open_fw_store_object(struct generic_update_agent *update_agent,
213 const struct uuid_octets *uuid, uint32_t *handle, int *status)
Julian Halle35efa52022-10-31 16:34:58 +0000214{
215 const uint8_t *exported_data;
216 size_t exported_data_len;
217
Gyorgy Szing3c446242023-03-31 01:53:15 +0200218 if (fw_store_export(update_agent->fw_store, uuid, &exported_data, &exported_data_len,
219 status)) {
Julian Halle35efa52022-10-31 16:34:58 +0000220 if (*status == FWU_STATUS_SUCCESS) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200221 *status = stream_manager_open_buffer_stream(&update_agent->stream_manager,
222 exported_data,
223 exported_data_len, handle);
Julian Halle35efa52022-10-31 16:34:58 +0000224 }
225
226 return true;
227 }
228
229 return false;
230}
231
Imre Kisefadd3a2024-06-28 14:15:57 +0200232static bool open_fw_image(struct generic_update_agent *update_agent, const struct uuid_octets *uuid,
Gyorgy Szing3c446242023-03-31 01:53:15 +0200233 uint32_t *handle, int *status)
Julian Halle35efa52022-10-31 16:34:58 +0000234{
Gyorgy Szing3c446242023-03-31 01:53:15 +0200235 const struct image_info *image_info =
236 fw_directory_find_image_info(&update_agent->fw_directory, uuid);
Julian Halle35efa52022-10-31 16:34:58 +0000237
238 if (image_info) {
Julian Halle35efa52022-10-31 16:34:58 +0000239 if (update_agent->state == FWU_STATE_STAGING) {
Julian Halle35efa52022-10-31 16:34:58 +0000240 struct installer *installer;
241
Gyorgy Szing3c446242023-03-31 01:53:15 +0200242 *status = fw_store_select_installer(update_agent->fw_store, image_info,
243 &installer);
Julian Halle35efa52022-10-31 16:34:58 +0000244
245 if (*status == FWU_STATUS_SUCCESS) {
Julian Halle35efa52022-10-31 16:34:58 +0000246 *status = stream_manager_open_install_stream(
Gyorgy Szing3c446242023-03-31 01:53:15 +0200247 &update_agent->stream_manager, update_agent->fw_store,
248 installer, image_info, handle);
Julian Halle35efa52022-10-31 16:34:58 +0000249 }
250 } else {
Julian Halle35efa52022-10-31 16:34:58 +0000251 /* Attempting to open a fw image when not staging */
252 *status = FWU_STATUS_DENIED;
253 }
254
255 return true;
256 }
257
258 return false;
259}
Imre Kisefadd3a2024-06-28 14:15:57 +0200260
261static int open(void *context, const struct uuid_octets *uuid, uint8_t op_type, uint32_t *handle)
262{
263 int status = FWU_STATUS_SUCCESS;
264 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
265
266 /* Pass UUID along a chain-of-responsibility until it's handled */
267 if (!open_image_directory(update_agent, uuid, handle, &status) &&
268 !open_fw_store_object(update_agent, uuid, handle, &status) &&
269 !open_fw_image(update_agent, uuid, handle, &status)) {
270 /* UUID not recognised */
271 status = FWU_STATUS_UNKNOWN;
272 }
273
274 return status;
275}
276
277static int commit(void *context, uint32_t handle, bool accepted, uint32_t max_atomic_len,
278 uint32_t *progress, uint32_t *total_work)
279{
280 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
281 int result = 0;
282
283 result = stream_manager_close(&update_agent->stream_manager, handle, accepted);
284 if (!result)
285 *progress = 1;
286
287 *total_work = 1;
288
289 return result;
290}
291
292static int write_stream(void *context, uint32_t handle, const uint8_t *data, size_t data_len)
293{
294 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
295
296 return stream_manager_write(&update_agent->stream_manager, handle, data, data_len);
297}
298
299static int read_stream(void *context, uint32_t handle, uint8_t *buf, size_t buf_size,
300 size_t *read_len, size_t *total_len)
301{
302 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
303
304 return stream_manager_read(&update_agent->stream_manager, handle, buf, buf_size, read_len,
305 total_len);
306}
307
308
309static const struct update_agent_interface interface = {
Imre Kis18b387d2024-07-02 13:51:07 +0200310 .discover = discover,
Imre Kisefadd3a2024-06-28 14:15:57 +0200311 .begin_staging = begin_staging,
312 .end_staging = end_staging,
313 .cancel_staging = cancel_staging,
314 .open = open,
315 .write_stream = write_stream,
316 .read_stream = read_stream,
317 .commit = commit,
318 .accept_image = accept,
319 .select_previous = select_previous,
320};
321
322static void deinit_context(struct generic_update_agent *context)
323{
324 if (!context)
325 return;
326
327 stream_manager_deinit(&context->stream_manager);
328 fw_directory_deinit(&context->fw_directory);
329
330 if (context->image_dir_buf)
331 free(context->image_dir_buf);
332
333 free(context);
334}
335
336struct update_agent *update_agent_init(unsigned int boot_index,
337 fw_inspector_inspect fw_inspect_method,
338 struct fw_store *fw_store)
339{
340 int status = FWU_STATUS_UNKNOWN;
341 struct generic_update_agent *context = NULL;
342 struct update_agent *agent = NULL;
343
344 assert(fw_inspect_method);
345 assert(fw_store);
346
347 context = (struct generic_update_agent *)calloc(1, sizeof(*context));
348 if (!context) {
349 DMSG("Failed to allocate update agent context");
350 return NULL;
351 }
352
353 context->state = FWU_STATE_INITIALIZING;
354 context->fw_inspect_method = fw_inspect_method;
355 context->fw_store = fw_store;
356 context->image_dir_buf_size = 0;
357 context->image_dir_buf = NULL;
358
359 stream_manager_init(&context->stream_manager);
360
361 /* Initialize and populate the fw_directory. The fw_inspector will
362 * obtain trustworthy information about the booted firmware and
363 * populate the fw_directory to reflect information about the booted
364 * firmware.
365 */
366 fw_directory_init(&context->fw_directory);
367
368 status = context->fw_inspect_method(&context->fw_directory, boot_index);
369 if (status != FWU_STATUS_SUCCESS) {
370 DMSG("Failed to run FW inspect: %d", status);
371 deinit_context(context);
372 return NULL;
373 }
374
375 /* Allow the associated fw_store to synchronize its state to the
376 * state of the booted firmware reflected by the fw_directory.
377 */
378 status = fw_store_synchronize(context->fw_store, &context->fw_directory, boot_index);
379 if (status != FWU_STATUS_SUCCESS) {
380 DMSG("Failed synchronize FW store: %d", status);
381 deinit_context(context);
382 return NULL;
383 }
384
385 /* Allocate a buffer for holding the serialized image directory */
386 context->image_dir_buf_size = img_dir_serializer_get_len(&context->fw_directory);
387 context->image_dir_buf = malloc(context->image_dir_buf_size);
388 if (!context->image_dir_buf) {
389 DMSG("Failed to allocate image_dir_buf");
390 deinit_context(context);
391 return NULL;
392 }
393
394 /* Transition to initial state */
395 context->state = fw_store_is_trial(context->fw_store) ? FWU_STATE_TRIAL : FWU_STATE_REGULAR;
396
397 agent = (struct update_agent *)calloc(1, sizeof(*agent));
398 if (!agent) {
399 DMSG("Failed to allocate update_agent");
400 deinit_context(context);
401 return NULL;
402 }
403
404 agent->context = context;
405 agent->interface = &interface;
406
407 return agent;
408}
409
410void update_agent_deinit(struct update_agent *update_agent)
411{
412 struct generic_update_agent *context = (struct generic_update_agent *)update_agent->context;
413
414 deinit_context(context);
415 free(update_agent);
416}