blob: 5284f71cf2e3925516997ed28fda8d32f3ed0a47 [file] [log] [blame]
Julian Halle35efa52022-10-31 16:34:58 +00001/*
2 * Copyright (c) 2022, Arm Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <stddef.h>
8#include <string.h>
9#include <protocols/service/fwu/packed-c/status.h>
10#include <service/fwu/fw_store/fw_store.h>
11#include "stream_manager.h"
12
13static uint32_t generate_handle(
14 struct stream_manager *subject,
15 struct stream_context *context)
16{
17 /* Handle includes rolling count value to protect against use of a stale handle */
18 uint32_t new_handle = context - subject->contexts;
19
20 new_handle = (new_handle & 0xffff) | (subject->rolling_count << 16);
21 ++subject->rolling_count;
22 return new_handle;
23}
24
25static uint32_t index_from_handle(uint32_t handle)
26{
27 return handle & 0xffff;
28}
29
30static void add_to_free_list(
31 struct stream_manager *subject,
32 struct stream_context *context)
33{
34 context->type = FWU_STREAM_TYPE_NONE;
35 context->handle = 0;
36 context->next = subject->free;
37 context->prev = NULL;
38 subject->free = context;
39}
40
41static struct stream_context *alloc_stream_context(
42 struct stream_manager *subject,
43 enum fwu_stream_type type,
44 uint32_t *handle)
45{
46 struct stream_context *context = NULL;
47
48 /* Re-cycle least-recently used context if there are no free contexts */
49 if (!subject->free && subject->active_tail) {
50
51 stream_manager_close(
52 subject,
53 subject->active_tail->handle,
54 false);
55 }
56
57 /* Active contexts are held in a linked list in most recently allocated order */
58 if (subject->free) {
59
60 context = subject->free;
61 subject->free = context->next;
62
63 context->next = subject->active_head;
64 context->prev = NULL;
65 subject->active_head = context;
66
67 if (!subject->active_tail)
68 subject->active_tail = context;
69
70 if (context->next)
71 context->next->prev = context;
72
73 context->type = type;
74
75 context->handle = generate_handle(subject, context);
76 *handle = context->handle;
77 }
78
79 return context;
80}
81
82static void free_stream_context(
83 struct stream_manager *subject,
84 struct stream_context *context)
85{
86 /* Remove from active list */
87 if (context->prev)
88 context->prev->next = context->next;
89 else
90 subject->active_head = context->next;
91
92 if (context->next)
93 context->next->prev = context->prev;
94 else
95 subject->active_tail = context->prev;
96
97 /* Add to free list */
98 add_to_free_list(subject, context);
99}
100
101static struct stream_context *get_active_context(
102 struct stream_manager *subject,
103 uint32_t handle)
104{
105 struct stream_context *context = NULL;
106 uint32_t index = index_from_handle(handle);
107
108 if ((index < FWU_STREAM_MANAGER_POOL_SIZE) &&
109 (subject->contexts[index].type != FWU_STREAM_TYPE_NONE) &&
110 (subject->contexts[index].handle == handle)) {
111
112 /* Handle qualifies an active stream context */
113 context = &subject->contexts[index];
114 }
115
116 return context;
117}
118
119void stream_manager_init(
120 struct stream_manager *subject)
121{
122 subject->free = NULL;
123 subject->active_head = NULL;
124 subject->active_tail = NULL;
125 subject->rolling_count = 0;
126
127 for (size_t i = 0; i < FWU_STREAM_MANAGER_POOL_SIZE; i++)
128 add_to_free_list(subject, &subject->contexts[i]);
129}
130
131void stream_manager_deinit(
132 struct stream_manager *subject)
133{
134 (void)subject;
135}
136
137int stream_manager_open_buffer_stream(
138 struct stream_manager *subject,
139 const uint8_t *data,
140 size_t data_len,
141 uint32_t *handle)
142{
143 struct stream_context *context;
144 int status = FWU_STATUS_UNKNOWN;
145
146 /* First cancel any buffer streams left open associated with
147 * the same source buffer. Concurrent stream access to data in
148 * a buffer is prevented to avoid the possibility of a buffer
149 * being updated while a read stream is open.
150 */
151 for (size_t i = 0; i < FWU_STREAM_MANAGER_POOL_SIZE; i++) {
152
153 context = &subject->contexts[i];
154
155 if ((context->type == FWU_STREAM_TYPE_BUFFER) &&
156 (context->variant.buffer.data == data))
157 free_stream_context(subject, context);
158 }
159
160 /* Allocate and initialize a new stream */
161 context = alloc_stream_context(
162 subject,
163 FWU_STREAM_TYPE_BUFFER,
164 handle);
165
166 if (context) {
167
168 context->variant.buffer.data = data;
169 context->variant.buffer.data_len = data_len;
170 context->variant.buffer.pos = 0;
171
172 status = FWU_STATUS_SUCCESS;
173 }
174
175 return status;
176}
177
178int stream_manager_open_install_stream(
179 struct stream_manager *subject,
180 struct fw_store *fw_store,
181 struct installer *installer,
182 const struct image_info *image_info,
183 uint32_t *stream_handle)
184{
185 struct stream_context *context;
186 int status = FWU_STATUS_UNKNOWN;
187
188 /* First cancel any install streams left open associated with
189 * the same fw_store and installer. This defends against the
190 * possibility of data written via two streams being written to the
191 * same installer, resulting in image corruption.
192 */
193 for (size_t i = 0; i < FWU_STREAM_MANAGER_POOL_SIZE; i++) {
194
195 context = &subject->contexts[i];
196
197 if ((context->type == FWU_STREAM_TYPE_INSTALL) &&
198 (context->variant.install.fw_store == fw_store) &&
199 (context->variant.install.installer == installer))
200 free_stream_context(subject, context);
201 }
202
203 /* Allocate and initialize a new stream */
204 context = alloc_stream_context(
205 subject,
206 FWU_STREAM_TYPE_INSTALL,
207 stream_handle);
208
209 if (context) {
210
211 context->variant.install.fw_store = fw_store;
212 context->variant.install.installer = installer;
213 context->variant.install.image_info = image_info;
214
215 status = FWU_STATUS_SUCCESS;
216 }
217
218 return status;
219}
220
221int stream_manager_close(
222 struct stream_manager *subject,
223 uint32_t handle,
224 bool accepted)
225{
226 int status = FWU_STATUS_UNKNOWN;
227 struct stream_context *context = get_active_context(subject, handle);
228
229 if (context) {
230
231 status = FWU_STATUS_SUCCESS;
232
233 if (context->type == FWU_STREAM_TYPE_INSTALL) {
234
235 status = fw_store_commit_image(
236 context->variant.install.fw_store,
237 context->variant.install.installer,
238 context->variant.install.image_info,
239 accepted);
240 }
241
242 free_stream_context(subject, context);
243 }
244
245 return status;
246}
247
248void stream_manager_cancel_streams(
249 struct stream_manager *subject,
250 enum fwu_stream_type type)
251{
252 for (size_t i = 0; i < FWU_STREAM_MANAGER_POOL_SIZE; i++) {
253
254 struct stream_context *context = &subject->contexts[i];
255
256 if (context->type == type)
257 free_stream_context(subject, context);
258 }
259}
260
261bool stream_manager_is_open_streams(
262 const struct stream_manager *subject,
263 enum fwu_stream_type type)
264{
265 bool any_open = false;
266
267 for (size_t i = 0; i < FWU_STREAM_MANAGER_POOL_SIZE; i++) {
268
269 const struct stream_context *context = &subject->contexts[i];
270
271 if (context->type == type) {
272
273 any_open = true;
274 break;
275 }
276 }
277
278 return any_open;
279}
280
281int stream_manager_write(
282 struct stream_manager *subject,
283 uint32_t handle,
284 const uint8_t *data,
285 size_t data_len)
286{
287 int status = FWU_STATUS_UNKNOWN;
288 struct stream_context *context = get_active_context(subject, handle);
289
290 if (context && context->type == FWU_STREAM_TYPE_INSTALL) {
291
292 status = fw_store_write_image(
293 context->variant.install.fw_store,
294 context->variant.install.installer,
295 data, data_len);
296 }
297
298 return status;
299}
300
301int stream_manager_read(
302 struct stream_manager *subject,
303 uint32_t handle,
304 uint8_t *buf,
305 size_t buf_size,
306 size_t *read_len,
307 size_t *total_len)
308{
309 int status = FWU_STATUS_UNKNOWN;
310 struct stream_context *context = get_active_context(subject, handle);
311
312 if (context) {
313
314 if (context->type == FWU_STREAM_TYPE_BUFFER) {
315
316 size_t pos = context->variant.buffer.pos;
317 size_t remaining_len = context->variant.buffer.data_len - pos;
318 size_t len_to_read =
319 (remaining_len <= buf_size) ? remaining_len : buf_size;
320
321 memcpy(buf, &context->variant.buffer.data[pos], len_to_read);
322
323 *read_len = len_to_read;
324 *total_len = context->variant.buffer.data_len;
325 context->variant.buffer.pos = pos + len_to_read;
326
327 status = FWU_STATUS_SUCCESS;
328 } else {
329
330 /* Reading from other types of stream is forbidden */
331 status = FWU_STATUS_DENIED;
332 }
333 }
334
335 return status;
336}