blob: c6bcc773f797227dbc42629e1715ff373b494755 [file] [log] [blame]
Manish Pandey65fe3642025-03-21 12:44:42 +00001/*
2 * Copyright The Transfer List Library Contributors
3 *
4 * SPDX-License-Identifier: MIT OR GPL-2.0-or-later
5 */
6
7#include <assert.h>
8#include <inttypes.h>
9#include <stdio.h>
10#include <string.h>
11
Harrison Mutai789db582025-04-28 14:46:49 +000012#include <logging.h>
Harrison Mutaibd05e572025-04-16 14:20:35 +000013#include <private/math_utils.h>
Manish Pandey65fe3642025-03-21 12:44:42 +000014#include <transfer_list.h>
15
Govindraj Raja442c09a2025-04-10 16:30:22 +020016void transfer_list_dump(struct transfer_list_header *tl)
17{
Manish Pandey65fe3642025-03-21 12:44:42 +000018 struct transfer_list_entry *te = NULL;
19 int i = 0;
20
21 if (!tl) {
22 return;
23 }
Harrison Mutai789db582025-04-28 14:46:49 +000024 info("Dump transfer list:\n");
25 info("signature 0x%x\n", tl->signature);
26 info("checksum 0x%x\n", tl->checksum);
27 info("version 0x%x\n", tl->version);
28 info("hdr_size 0x%x\n", tl->hdr_size);
29 info("alignment 0x%x\n", tl->alignment);
30 info("size 0x%x\n", tl->size);
31 info("max_size 0x%x\n", tl->max_size);
32 info("flags 0x%x\n", tl->flags);
Manish Pandey65fe3642025-03-21 12:44:42 +000033 while (true) {
34 te = transfer_list_next(tl, te);
35 if (!te) {
36 break;
37 }
Harrison Mutaidb83bfa2025-03-21 15:24:55 +000038
Harrison Mutai789db582025-04-28 14:46:49 +000039 info("Entry %d:\n", i++);
Harrison Mutaidb83bfa2025-03-21 15:24:55 +000040 transfer_entry_dump(te);
41 }
42}
43
44void transfer_entry_dump(struct transfer_list_entry *te)
45{
46 if (te) {
Harrison Mutai789db582025-04-28 14:46:49 +000047 info("tag_id 0x%x\n", te->tag_id);
48 info("hdr_size 0x%x\n", te->hdr_size);
49 info("data_size 0x%x\n", te->data_size);
50 info("data_addr 0x%lx\n",
51 (unsigned long)transfer_list_entry_data(te));
Manish Pandey65fe3642025-03-21 12:44:42 +000052 }
53}
54
Manish Pandey65fe3642025-03-21 12:44:42 +000055struct transfer_list_header *transfer_list_init(void *addr, size_t max_size)
56{
57 struct transfer_list_header *tl = addr;
58
59 if (!addr || max_size == 0) {
60 return NULL;
61 }
62
Harrison Mutaibd05e572025-04-16 14:20:35 +000063 if (!libtl_is_aligned((uintptr_t)addr,
64 1 << TRANSFER_LIST_INIT_MAX_ALIGN) ||
65 !libtl_is_aligned(max_size, 1 << TRANSFER_LIST_INIT_MAX_ALIGN) ||
Manish Pandey65fe3642025-03-21 12:44:42 +000066 max_size < sizeof(*tl)) {
67 return NULL;
68 }
69
70 memset(tl, 0, max_size);
71 tl->signature = TRANSFER_LIST_SIGNATURE;
72 tl->version = TRANSFER_LIST_VERSION;
73 tl->hdr_size = sizeof(*tl);
74 tl->alignment = TRANSFER_LIST_INIT_MAX_ALIGN; /* initial max align */
75 tl->size = sizeof(*tl); /* initial size is the size of header */
76 tl->max_size = max_size;
77 tl->flags = TL_FLAGS_HAS_CHECKSUM;
78
79 transfer_list_update_checksum(tl);
80
81 return tl;
82}
83
Manish Pandey65fe3642025-03-21 12:44:42 +000084struct transfer_list_header *
85transfer_list_relocate(struct transfer_list_header *tl, void *addr,
86 size_t max_size)
87{
88 uintptr_t new_addr, align_mask, align_off;
89 struct transfer_list_header *new_tl;
90 uint32_t new_max_size;
91
92 if (!tl || !addr || max_size == 0) {
93 return NULL;
94 }
95
96 align_mask = (1 << tl->alignment) - 1;
97 align_off = (uintptr_t)tl & align_mask;
98 new_addr = ((uintptr_t)addr & ~align_mask) + align_off;
99
100 if (new_addr < (uintptr_t)addr) {
101 new_addr += (1 << tl->alignment);
102 }
103
104 new_max_size = max_size - (new_addr - (uintptr_t)addr);
105
106 /* the new space is not sufficient for the tl */
107 if (tl->size > new_max_size) {
108 return NULL;
109 }
110
111 new_tl = (struct transfer_list_header *)new_addr;
112 memmove(new_tl, tl, tl->size);
113 new_tl->max_size = new_max_size;
114
115 transfer_list_update_checksum(new_tl);
116
117 return new_tl;
118}
119
Manish Pandey65fe3642025-03-21 12:44:42 +0000120enum transfer_list_ops
121transfer_list_check_header(const struct transfer_list_header *tl)
122{
123 if (!tl) {
124 return TL_OPS_NON;
125 }
126
127 if (tl->signature != TRANSFER_LIST_SIGNATURE) {
Harrison Mutai789db582025-04-28 14:46:49 +0000128 warn("Bad transfer list signature %#" PRIx32 "\n",
129 tl->signature);
Manish Pandey65fe3642025-03-21 12:44:42 +0000130 return TL_OPS_NON;
131 }
132
133 if (!tl->max_size) {
Harrison Mutai789db582025-04-28 14:46:49 +0000134 warn("Bad transfer list max size %#" PRIx32 "\n", tl->max_size);
Manish Pandey65fe3642025-03-21 12:44:42 +0000135 return TL_OPS_NON;
136 }
137
138 if (tl->size > tl->max_size) {
Harrison Mutai789db582025-04-28 14:46:49 +0000139 warn("Bad transfer list size %#" PRIx32 "\n", tl->size);
Manish Pandey65fe3642025-03-21 12:44:42 +0000140 return TL_OPS_NON;
141 }
142
143 if (tl->hdr_size != sizeof(struct transfer_list_header)) {
Harrison Mutai789db582025-04-28 14:46:49 +0000144 warn("Bad transfer list header size %#" PRIx32 "\n",
145 tl->hdr_size);
Manish Pandey65fe3642025-03-21 12:44:42 +0000146 return TL_OPS_NON;
147 }
148
149 if (!transfer_list_verify_checksum(tl)) {
Harrison Mutai789db582025-04-28 14:46:49 +0000150 warn("Bad transfer list checksum %#" PRIx32 "\n", tl->checksum);
Manish Pandey65fe3642025-03-21 12:44:42 +0000151 return TL_OPS_NON;
152 }
153
154 if (tl->version == 0) {
Harrison Mutai789db582025-04-28 14:46:49 +0000155 warn("Transfer list version is invalid\n");
Manish Pandey65fe3642025-03-21 12:44:42 +0000156 return TL_OPS_NON;
157 } else if (tl->version == TRANSFER_LIST_VERSION) {
Harrison Mutai789db582025-04-28 14:46:49 +0000158 info("Transfer list version is valid for all operations\n");
Manish Pandey65fe3642025-03-21 12:44:42 +0000159 return TL_OPS_ALL;
160 } else if (tl->version > TRANSFER_LIST_VERSION) {
Harrison Mutai789db582025-04-28 14:46:49 +0000161 info("Transfer list version is valid for read-only\n");
Manish Pandey65fe3642025-03-21 12:44:42 +0000162 return TL_OPS_RO;
163 }
164
Harrison Mutai789db582025-04-28 14:46:49 +0000165 info("Old transfer list version is detected\n");
Manish Pandey65fe3642025-03-21 12:44:42 +0000166 return TL_OPS_CUS;
167}
168
Manish Pandey65fe3642025-03-21 12:44:42 +0000169struct transfer_list_entry *transfer_list_next(struct transfer_list_header *tl,
170 struct transfer_list_entry *last)
171{
172 struct transfer_list_entry *te = NULL;
173 uintptr_t tl_ev = 0;
174 uintptr_t va = 0;
175 uintptr_t ev = 0;
176 size_t sz = 0;
177
178 if (!tl) {
179 return NULL;
180 }
181
182 tl_ev = (uintptr_t)tl + tl->size;
183
184 if (last) {
185 va = (uintptr_t)last;
186 /* check if the total size overflow */
Harrison Mutaibd05e572025-04-16 14:20:35 +0000187 if (libtl_add_overflow(last->hdr_size, last->data_size, &sz)) {
Manish Pandey65fe3642025-03-21 12:44:42 +0000188 return NULL;
189 }
190 /* roundup to the next entry */
Harrison Mutaibd05e572025-04-16 14:20:35 +0000191 if (libtl_add_with_round_up_overflow(
192 va, sz, TRANSFER_LIST_GRANULE, &va)) {
Manish Pandey65fe3642025-03-21 12:44:42 +0000193 return NULL;
194 }
195 } else {
196 va = (uintptr_t)tl + tl->hdr_size;
197 }
198
199 te = (struct transfer_list_entry *)va;
200
201 if (va + sizeof(*te) > tl_ev || te->hdr_size < sizeof(*te) ||
Harrison Mutaibd05e572025-04-16 14:20:35 +0000202 libtl_add_overflow(te->hdr_size, te->data_size, &sz) ||
203 libtl_add_overflow(va, sz, &ev) || ev > tl_ev) {
Manish Pandey65fe3642025-03-21 12:44:42 +0000204 return NULL;
205 }
206
207 return te;
208}
209
210/*******************************************************************************
Yeoreum Yunb7dc0de2025-06-02 20:38:19 +0100211 * Enumerate the prev transfer entry
212 * Return pointer to the prev transfer entry or NULL on error
213 ******************************************************************************/
214struct transfer_list_entry *transfer_list_prev(struct transfer_list_header *tl,
215 struct transfer_list_entry *last)
216{
217 struct transfer_list_entry *prev;
218 struct transfer_list_entry *te = NULL;
219
220 if (!last || !tl || (tl + tl->hdr_size == (void *)last)) {
221 return NULL;
222 }
223
224 do {
225 prev = te;
226 te = transfer_list_next(tl, te);
227 } while (te && te != last);
228
229 return (te != NULL) ? prev : NULL;
230}
231
232/*******************************************************************************
Manish Pandey65fe3642025-03-21 12:44:42 +0000233 * Calculate the byte sum of a transfer list
234 * Return byte sum of the transfer list
235 ******************************************************************************/
236static uint8_t calc_byte_sum(const struct transfer_list_header *tl)
237{
238 uint8_t *b = (uint8_t *)tl;
239 uint8_t cs = 0;
240 size_t n = 0;
241
242 for (n = 0; n < tl->size; n++) {
243 cs += b[n];
244 }
245
246 return cs;
247}
248
Manish Pandey65fe3642025-03-21 12:44:42 +0000249void transfer_list_update_checksum(struct transfer_list_header *tl)
250{
251 uint8_t cs;
252
253 if (!tl || !(tl->flags & TL_FLAGS_HAS_CHECKSUM)) {
254 return;
255 }
256
257 cs = calc_byte_sum(tl);
258 cs -= tl->checksum;
259 cs = 256 - cs;
260 tl->checksum = cs;
261 assert(transfer_list_verify_checksum(tl));
262}
263
Manish Pandey65fe3642025-03-21 12:44:42 +0000264bool transfer_list_verify_checksum(const struct transfer_list_header *tl)
265{
266 if (!tl) {
267 return false;
268 }
269
270 if (!(tl->flags & TL_FLAGS_HAS_CHECKSUM)) {
271 return true;
272 }
273
274 return !calc_byte_sum(tl);
275}
276
Manish Pandey65fe3642025-03-21 12:44:42 +0000277bool transfer_list_set_data_size(struct transfer_list_header *tl,
278 struct transfer_list_entry *te,
279 uint32_t new_data_size)
280{
Yeoreum Yunb7dc0de2025-06-02 20:38:19 +0100281 uintptr_t tl_old_ev, new_ev = 0, old_ev = 0, merge_ev, ru_new_ev;
Manish Pandey65fe3642025-03-21 12:44:42 +0000282 struct transfer_list_entry *dummy_te = NULL;
283 size_t gap = 0;
284 size_t mov_dis = 0;
285 size_t sz = 0;
286
287 if (!tl || !te) {
288 return false;
289 }
290 tl_old_ev = (uintptr_t)tl + tl->size;
291
292 /*
293 * calculate the old and new end of TE
294 * both must be roundup to align with TRANSFER_LIST_GRANULE
295 */
Harrison Mutaibd05e572025-04-16 14:20:35 +0000296 if (libtl_add_overflow(te->hdr_size, te->data_size, &sz) ||
297 libtl_add_with_round_up_overflow((uintptr_t)te, sz,
298 TRANSFER_LIST_GRANULE, &old_ev)) {
Manish Pandey65fe3642025-03-21 12:44:42 +0000299 return false;
300 }
Harrison Mutaibd05e572025-04-16 14:20:35 +0000301 if (libtl_add_overflow(te->hdr_size, new_data_size, &sz) ||
302 libtl_add_with_round_up_overflow((uintptr_t)te, sz,
303 TRANSFER_LIST_GRANULE, &new_ev)) {
Manish Pandey65fe3642025-03-21 12:44:42 +0000304 return false;
305 }
306
307 if (new_ev > old_ev) {
308 /*
Yeoreum Yunb7dc0de2025-06-02 20:38:19 +0100309 * When next transfer list is dummy,
310 * - if te can be extended in boundary of dummy entry,
311 * extend it to dummy entry and set new dummy entry.
312 *
313 * - otherwise, merge dummy entry with existing te and
314 * extend transfer list as much as it requires.
315 */
316 dummy_te = transfer_list_next(tl, te);
317 if (dummy_te && (dummy_te->tag_id == TL_TAG_EMPTY)) {
Harrison Mutaibd05e572025-04-16 14:20:35 +0000318 merge_ev = libtl_align_up(old_ev + dummy_te->hdr_size +
319 dummy_te->data_size,
320 TRANSFER_LIST_GRANULE);
Yeoreum Yunb7dc0de2025-06-02 20:38:19 +0100321 if (merge_ev >= new_ev) {
322 gap = merge_ev - new_ev;
323 goto set_dummy;
324 } else {
325 old_ev = merge_ev;
326 }
327 }
328
329 /*
Manish Pandey65fe3642025-03-21 12:44:42 +0000330 * move distance should be roundup
331 * to meet the requirement of TE data max alignment
332 * ensure that the increased size doesn't exceed
333 * the max size of TL
334 */
335 mov_dis = new_ev - old_ev;
Harrison Mutaibd05e572025-04-16 14:20:35 +0000336 if (libtl_round_up_overflow(mov_dis, 1 << tl->alignment,
337 &mov_dis) ||
Manish Pandey65fe3642025-03-21 12:44:42 +0000338 tl->size + mov_dis > tl->max_size) {
339 return false;
340 }
341 ru_new_ev = old_ev + mov_dis;
342 memmove((void *)ru_new_ev, (void *)old_ev, tl_old_ev - old_ev);
343 tl->size += mov_dis;
344 gap = ru_new_ev - new_ev;
345 } else {
346 gap = old_ev - new_ev;
347 }
348
Yeoreum Yunb7dc0de2025-06-02 20:38:19 +0100349set_dummy:
Manish Pandey65fe3642025-03-21 12:44:42 +0000350 if (gap >= sizeof(*dummy_te)) {
351 /* create a dummy TE to fill up the gap */
352 dummy_te = (struct transfer_list_entry *)new_ev;
353 dummy_te->tag_id = TL_TAG_EMPTY;
354 dummy_te->hdr_size = sizeof(*dummy_te);
355 dummy_te->data_size = gap - sizeof(*dummy_te);
356 }
357
358 te->data_size = new_data_size;
359
360 transfer_list_update_checksum(tl);
361 return true;
362}
363
Manish Pandey65fe3642025-03-21 12:44:42 +0000364bool transfer_list_rem(struct transfer_list_header *tl,
365 struct transfer_list_entry *te)
366{
Yeoreum Yunb7dc0de2025-06-02 20:38:19 +0100367 struct transfer_list_entry *prev;
368 struct transfer_list_entry *next;
369
Manish Pandey65fe3642025-03-21 12:44:42 +0000370 if (!tl || !te || (uintptr_t)te > (uintptr_t)tl + tl->size) {
371 return false;
372 }
Yeoreum Yunb7dc0de2025-06-02 20:38:19 +0100373
374 prev = transfer_list_prev(tl, te);
375 next = transfer_list_next(tl, te);
376
377 if (prev && prev->tag_id == TL_TAG_EMPTY) {
Harrison Mutaibd05e572025-04-16 14:20:35 +0000378 prev->data_size += libtl_align_up(te->hdr_size + te->data_size,
379 TRANSFER_LIST_GRANULE);
Yeoreum Yunb7dc0de2025-06-02 20:38:19 +0100380 te = prev;
381 }
382
383 if (next && next->tag_id == TL_TAG_EMPTY) {
Harrison Mutaibd05e572025-04-16 14:20:35 +0000384 te->data_size +=
385 libtl_align_up(next->hdr_size + next->data_size,
386 TRANSFER_LIST_GRANULE);
Yeoreum Yunb7dc0de2025-06-02 20:38:19 +0100387 }
388
Manish Pandey65fe3642025-03-21 12:44:42 +0000389 te->tag_id = TL_TAG_EMPTY;
390 transfer_list_update_checksum(tl);
391 return true;
392}
393
Manish Pandey65fe3642025-03-21 12:44:42 +0000394struct transfer_list_entry *transfer_list_add(struct transfer_list_header *tl,
395 uint32_t tag_id,
396 uint32_t data_size,
397 const void *data)
398{
Harrison Mutai7fb41222025-04-16 14:18:46 +0000399 uintptr_t tl_ev;
Manish Pandey65fe3642025-03-21 12:44:42 +0000400 struct transfer_list_entry *te = NULL;
401 uint8_t *te_data = NULL;
402 uintptr_t te_end;
Manish Pandey65fe3642025-03-21 12:44:42 +0000403
404 if (!tl || (tag_id & (1 << 24))) {
405 return NULL;
406 }
407
408 /*
409 * skip the step 1 (optional step)
410 * new TE will be added into the tail
411 */
412 tl_ev = (uintptr_t)tl + tl->size;
Harrison Mutaibd05e572025-04-16 14:20:35 +0000413 te = (struct transfer_list_entry *)libtl_align_up(
414 tl_ev, TRANSFER_LIST_GRANULE);
Manish Pandey65fe3642025-03-21 12:44:42 +0000415
Harrison Mutaibd05e572025-04-16 14:20:35 +0000416 te_end = libtl_align_up((uintptr_t)te + sizeof(*te) + data_size,
417 TRANSFER_LIST_GRANULE);
Manish Pandey65fe3642025-03-21 12:44:42 +0000418
419 if (te_end > (uintptr_t)tl + tl->max_size) {
420 return NULL;
421 }
422
423 te->tag_id = tag_id;
424 te->hdr_size = sizeof(*te);
425 te->data_size = data_size;
426 tl->size += te_end - tl_ev;
427
428 if (data) {
429 /* get TE data pointer */
430 te_data = transfer_list_entry_data(te);
431 if (!te_data) {
432 return NULL;
433 }
434 memmove(te_data, data, data_size);
435 }
436
437 transfer_list_update_checksum(tl);
438
439 return te;
440}
441
Manish Pandey65fe3642025-03-21 12:44:42 +0000442struct transfer_list_entry *
443transfer_list_add_with_align(struct transfer_list_header *tl, uint32_t tag_id,
444 uint32_t data_size, const void *data,
445 uint8_t alignment)
446{
447 struct transfer_list_entry *te = NULL;
448 uintptr_t tl_ev, ev, new_tl_ev;
449 size_t dummy_te_data_sz = 0;
450
451 if (!tl) {
452 return NULL;
453 }
454
455 tl_ev = (uintptr_t)tl + tl->size;
456 ev = tl_ev + sizeof(struct transfer_list_entry);
457
Harrison Mutaibd05e572025-04-16 14:20:35 +0000458 if (!libtl_is_aligned(ev, 1 << alignment)) {
Manish Pandey65fe3642025-03-21 12:44:42 +0000459 /*
460 * TE data address is not aligned to the new alignment
461 * fill the gap with an empty TE as a placeholder before
462 * adding the desire TE
463 */
Harrison Mutaibd05e572025-04-16 14:20:35 +0000464 new_tl_ev = libtl_align_up(ev, 1 << alignment) -
Manish Pandey65fe3642025-03-21 12:44:42 +0000465 sizeof(struct transfer_list_entry);
466 dummy_te_data_sz =
467 new_tl_ev - tl_ev - sizeof(struct transfer_list_entry);
468 if (!transfer_list_add(tl, TL_TAG_EMPTY, dummy_te_data_sz,
469 NULL)) {
470 return NULL;
471 }
472 }
473
474 te = transfer_list_add(tl, tag_id, data_size, data);
475
476 if (alignment > tl->alignment) {
477 tl->alignment = alignment;
478 transfer_list_update_checksum(tl);
479 }
480
481 return te;
482}
483
Manish Pandey65fe3642025-03-21 12:44:42 +0000484struct transfer_list_entry *transfer_list_find(struct transfer_list_header *tl,
485 uint32_t tag_id)
486{
487 struct transfer_list_entry *te = NULL;
488
489 do {
490 te = transfer_list_next(tl, te);
491 } while (te && (te->tag_id != tag_id));
492
493 return te;
494}
495
Manish Pandey65fe3642025-03-21 12:44:42 +0000496void *transfer_list_entry_data(struct transfer_list_entry *entry)
497{
498 if (!entry) {
499 return NULL;
500 }
501 return (uint8_t *)entry + entry->hdr_size;
502}
Harrison Mutaidb83bfa2025-03-21 15:24:55 +0000503
Harrison Mutaidb83bfa2025-03-21 15:24:55 +0000504struct transfer_list_header *transfer_list_ensure(void *addr, size_t size)
505{
506 struct transfer_list_header *tl = NULL;
507
508 if (transfer_list_check_header(addr) == TL_OPS_ALL) {
509 return (struct transfer_list_header *)addr;
510 }
511
512 tl = transfer_list_init((void *)addr, size);
513
514 return tl;
515}