blob: 77037b1990fe95ce5c762cf2cba8f6436293a8cc [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 Kisefadd3a2024-06-28 14:15:57 +020057static int begin_staging(void *context, uint32_t vendor_flags, uint32_t partial_update_count,
58 const struct uuid_octets *update_guid)
Julian Halle35efa52022-10-31 16:34:58 +000059{
60 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +020061 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +000062
63 /* If already staging, any previous installation state is discarded */
Imre Kisefadd3a2024-06-28 14:15:57 +020064 cancel_staging(update_agent);
Julian Halle35efa52022-10-31 16:34:58 +000065
66 if (update_agent->state == FWU_STATE_REGULAR) {
Julian Halle35efa52022-10-31 16:34:58 +000067 status = fw_store_begin_install(update_agent->fw_store);
68
69 /* Check if ready to install images */
70 if (status == FWU_STATUS_SUCCESS)
71 update_agent->state = FWU_STATE_STAGING;
72 }
73
74 return status;
75}
76
Imre Kisefadd3a2024-06-28 14:15:57 +020077static int end_staging(void *context)
Julian Halle35efa52022-10-31 16:34:58 +000078{
79 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +020080 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +000081
82 if (update_agent->state == FWU_STATE_STAGING) {
Julian Halle35efa52022-10-31 16:34:58 +000083 /* The client is responsible for committing each installed image. If any
84 * install streams have been left open, not all images were committed.
85 */
Gyorgy Szing3c446242023-03-31 01:53:15 +020086 bool any_uncommitted = stream_manager_is_open_streams(&update_agent->stream_manager,
87 FWU_STREAM_TYPE_INSTALL);
Julian Halle35efa52022-10-31 16:34:58 +000088
89 if (!any_uncommitted) {
Julian Halle35efa52022-10-31 16:34:58 +000090 /* All installed images have been committed so we're
91 * ready for a trial.
92 */
93 status = fw_store_finalize_install(update_agent->fw_store);
94
95 if (status == FWU_STATUS_SUCCESS)
96 /* Transition to TRAIL_PENDING state. The trial actually starts
97 * when installed images are activated through a system restart.
98 */
99 update_agent->state = FWU_STATE_TRIAL_PENDING;
100
101 } else {
Julian Halle35efa52022-10-31 16:34:58 +0000102 /* Client failed to commit all images installed */
103 status = FWU_STATUS_BUSY;
104 }
105 }
106
107 return status;
108}
109
Imre Kisefadd3a2024-06-28 14:15:57 +0200110static int cancel_staging(void *context)
Julian Halle35efa52022-10-31 16:34:58 +0000111{
112 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +0200113 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +0000114
115 if (update_agent->state == FWU_STATE_STAGING) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200116 stream_manager_cancel_streams(&update_agent->stream_manager,
117 FWU_STREAM_TYPE_INSTALL);
Julian Halle35efa52022-10-31 16:34:58 +0000118
119 fw_store_cancel_install(update_agent->fw_store);
120
121 update_agent->state = FWU_STATE_REGULAR;
122
123 status = FWU_STATUS_SUCCESS;
124 }
125
126 return status;
127}
128
Imre Kisefadd3a2024-06-28 14:15:57 +0200129static int accept(void *context, const struct uuid_octets *image_type_uuid)
Julian Halle35efa52022-10-31 16:34:58 +0000130{
131 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +0200132 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +0000133
134 if (update_agent->state == FWU_STATE_TRIAL) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200135 const struct image_info *image_info =
136 fw_directory_find_image_info(&update_agent->fw_directory, image_type_uuid);
Julian Halle35efa52022-10-31 16:34:58 +0000137
138 if (image_info) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200139 if (fw_store_notify_accepted(update_agent->fw_store, image_info)) {
Julian Halle35efa52022-10-31 16:34:58 +0000140 /* From the fw_store perspective, the update has
141 * been fully accepted.
142 */
143 status = fw_store_commit_to_update(update_agent->fw_store);
144 update_agent->state = FWU_STATE_REGULAR;
145
146 } else
147 /* Still more images to accept */
148 status = FWU_STATUS_SUCCESS;
149 } else
150 /* Unrecognised image uuid */
151 status = FWU_STATUS_UNKNOWN;
152 }
153
154 return status;
155}
156
Imre Kisefadd3a2024-06-28 14:15:57 +0200157static int select_previous(void *context)
Julian Halle35efa52022-10-31 16:34:58 +0000158{
159 int status = FWU_STATUS_DENIED;
Imre Kisefadd3a2024-06-28 14:15:57 +0200160 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
Julian Halle35efa52022-10-31 16:34:58 +0000161
162 if ((update_agent->state == FWU_STATE_TRIAL) ||
Gyorgy Szing3c446242023-03-31 01:53:15 +0200163 (update_agent->state == FWU_STATE_TRIAL_PENDING)) {
Julian Halle35efa52022-10-31 16:34:58 +0000164 status = fw_store_revert_to_previous(update_agent->fw_store);
165 update_agent->state = FWU_STATE_REGULAR;
166 }
167
168 return status;
169}
170
Imre Kisefadd3a2024-06-28 14:15:57 +0200171static bool open_image_directory(struct generic_update_agent *update_agent,
172 const struct uuid_octets *uuid, uint32_t *handle, int *status)
Julian Halle35efa52022-10-31 16:34:58 +0000173{
174 struct uuid_octets target_uuid;
175
176 uuid_guid_octets_from_canonical(&target_uuid, FWU_DIRECTORY_CANONICAL_UUID);
177
178 if (uuid_is_equal(uuid->octets, target_uuid.octets)) {
Julian Halle35efa52022-10-31 16:34:58 +0000179 /* Serialize a fresh view of the image directory */
180 size_t serialized_len = 0;
181
Gyorgy Szing3c446242023-03-31 01:53:15 +0200182 *status = img_dir_serializer_serialize(&update_agent->fw_directory,
183 update_agent->fw_store,
184 update_agent->image_dir_buf,
185 update_agent->image_dir_buf_size,
186 &serialized_len);
Julian Halle35efa52022-10-31 16:34:58 +0000187
188 if (*status == FWU_STATUS_SUCCESS) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200189 *status = stream_manager_open_buffer_stream(&update_agent->stream_manager,
190 update_agent->image_dir_buf,
191 serialized_len, handle);
Julian Halle35efa52022-10-31 16:34:58 +0000192 }
193
194 return true;
195 }
196
197 return false;
198}
199
Imre Kisefadd3a2024-06-28 14:15:57 +0200200static bool open_fw_store_object(struct generic_update_agent *update_agent,
201 const struct uuid_octets *uuid, uint32_t *handle, int *status)
Julian Halle35efa52022-10-31 16:34:58 +0000202{
203 const uint8_t *exported_data;
204 size_t exported_data_len;
205
Gyorgy Szing3c446242023-03-31 01:53:15 +0200206 if (fw_store_export(update_agent->fw_store, uuid, &exported_data, &exported_data_len,
207 status)) {
Julian Halle35efa52022-10-31 16:34:58 +0000208 if (*status == FWU_STATUS_SUCCESS) {
Gyorgy Szing3c446242023-03-31 01:53:15 +0200209 *status = stream_manager_open_buffer_stream(&update_agent->stream_manager,
210 exported_data,
211 exported_data_len, handle);
Julian Halle35efa52022-10-31 16:34:58 +0000212 }
213
214 return true;
215 }
216
217 return false;
218}
219
Imre Kisefadd3a2024-06-28 14:15:57 +0200220static bool open_fw_image(struct generic_update_agent *update_agent, const struct uuid_octets *uuid,
Gyorgy Szing3c446242023-03-31 01:53:15 +0200221 uint32_t *handle, int *status)
Julian Halle35efa52022-10-31 16:34:58 +0000222{
Gyorgy Szing3c446242023-03-31 01:53:15 +0200223 const struct image_info *image_info =
224 fw_directory_find_image_info(&update_agent->fw_directory, uuid);
Julian Halle35efa52022-10-31 16:34:58 +0000225
226 if (image_info) {
Julian Halle35efa52022-10-31 16:34:58 +0000227 if (update_agent->state == FWU_STATE_STAGING) {
Julian Halle35efa52022-10-31 16:34:58 +0000228 struct installer *installer;
229
Gyorgy Szing3c446242023-03-31 01:53:15 +0200230 *status = fw_store_select_installer(update_agent->fw_store, image_info,
231 &installer);
Julian Halle35efa52022-10-31 16:34:58 +0000232
233 if (*status == FWU_STATUS_SUCCESS) {
Julian Halle35efa52022-10-31 16:34:58 +0000234 *status = stream_manager_open_install_stream(
Gyorgy Szing3c446242023-03-31 01:53:15 +0200235 &update_agent->stream_manager, update_agent->fw_store,
236 installer, image_info, handle);
Julian Halle35efa52022-10-31 16:34:58 +0000237 }
238 } else {
Julian Halle35efa52022-10-31 16:34:58 +0000239 /* Attempting to open a fw image when not staging */
240 *status = FWU_STATUS_DENIED;
241 }
242
243 return true;
244 }
245
246 return false;
247}
Imre Kisefadd3a2024-06-28 14:15:57 +0200248
249static int open(void *context, const struct uuid_octets *uuid, uint8_t op_type, uint32_t *handle)
250{
251 int status = FWU_STATUS_SUCCESS;
252 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
253
254 /* Pass UUID along a chain-of-responsibility until it's handled */
255 if (!open_image_directory(update_agent, uuid, handle, &status) &&
256 !open_fw_store_object(update_agent, uuid, handle, &status) &&
257 !open_fw_image(update_agent, uuid, handle, &status)) {
258 /* UUID not recognised */
259 status = FWU_STATUS_UNKNOWN;
260 }
261
262 return status;
263}
264
265static int commit(void *context, uint32_t handle, bool accepted, uint32_t max_atomic_len,
266 uint32_t *progress, uint32_t *total_work)
267{
268 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
269 int result = 0;
270
271 result = stream_manager_close(&update_agent->stream_manager, handle, accepted);
272 if (!result)
273 *progress = 1;
274
275 *total_work = 1;
276
277 return result;
278}
279
280static int write_stream(void *context, uint32_t handle, const uint8_t *data, size_t data_len)
281{
282 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
283
284 return stream_manager_write(&update_agent->stream_manager, handle, data, data_len);
285}
286
287static int read_stream(void *context, uint32_t handle, uint8_t *buf, size_t buf_size,
288 size_t *read_len, size_t *total_len)
289{
290 struct generic_update_agent *update_agent = (struct generic_update_agent *)context;
291
292 return stream_manager_read(&update_agent->stream_manager, handle, buf, buf_size, read_len,
293 total_len);
294}
295
296
297static const struct update_agent_interface interface = {
298 .discover = NULL,
299 .begin_staging = begin_staging,
300 .end_staging = end_staging,
301 .cancel_staging = cancel_staging,
302 .open = open,
303 .write_stream = write_stream,
304 .read_stream = read_stream,
305 .commit = commit,
306 .accept_image = accept,
307 .select_previous = select_previous,
308};
309
310static void deinit_context(struct generic_update_agent *context)
311{
312 if (!context)
313 return;
314
315 stream_manager_deinit(&context->stream_manager);
316 fw_directory_deinit(&context->fw_directory);
317
318 if (context->image_dir_buf)
319 free(context->image_dir_buf);
320
321 free(context);
322}
323
324struct update_agent *update_agent_init(unsigned int boot_index,
325 fw_inspector_inspect fw_inspect_method,
326 struct fw_store *fw_store)
327{
328 int status = FWU_STATUS_UNKNOWN;
329 struct generic_update_agent *context = NULL;
330 struct update_agent *agent = NULL;
331
332 assert(fw_inspect_method);
333 assert(fw_store);
334
335 context = (struct generic_update_agent *)calloc(1, sizeof(*context));
336 if (!context) {
337 DMSG("Failed to allocate update agent context");
338 return NULL;
339 }
340
341 context->state = FWU_STATE_INITIALIZING;
342 context->fw_inspect_method = fw_inspect_method;
343 context->fw_store = fw_store;
344 context->image_dir_buf_size = 0;
345 context->image_dir_buf = NULL;
346
347 stream_manager_init(&context->stream_manager);
348
349 /* Initialize and populate the fw_directory. The fw_inspector will
350 * obtain trustworthy information about the booted firmware and
351 * populate the fw_directory to reflect information about the booted
352 * firmware.
353 */
354 fw_directory_init(&context->fw_directory);
355
356 status = context->fw_inspect_method(&context->fw_directory, boot_index);
357 if (status != FWU_STATUS_SUCCESS) {
358 DMSG("Failed to run FW inspect: %d", status);
359 deinit_context(context);
360 return NULL;
361 }
362
363 /* Allow the associated fw_store to synchronize its state to the
364 * state of the booted firmware reflected by the fw_directory.
365 */
366 status = fw_store_synchronize(context->fw_store, &context->fw_directory, boot_index);
367 if (status != FWU_STATUS_SUCCESS) {
368 DMSG("Failed synchronize FW store: %d", status);
369 deinit_context(context);
370 return NULL;
371 }
372
373 /* Allocate a buffer for holding the serialized image directory */
374 context->image_dir_buf_size = img_dir_serializer_get_len(&context->fw_directory);
375 context->image_dir_buf = malloc(context->image_dir_buf_size);
376 if (!context->image_dir_buf) {
377 DMSG("Failed to allocate image_dir_buf");
378 deinit_context(context);
379 return NULL;
380 }
381
382 /* Transition to initial state */
383 context->state = fw_store_is_trial(context->fw_store) ? FWU_STATE_TRIAL : FWU_STATE_REGULAR;
384
385 agent = (struct update_agent *)calloc(1, sizeof(*agent));
386 if (!agent) {
387 DMSG("Failed to allocate update_agent");
388 deinit_context(context);
389 return NULL;
390 }
391
392 agent->context = context;
393 agent->interface = &interface;
394
395 return agent;
396}
397
398void update_agent_deinit(struct update_agent *update_agent)
399{
400 struct generic_update_agent *context = (struct generic_update_agent *)update_agent->context;
401
402 deinit_context(context);
403 free(update_agent);
404}