1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 #include "compiler.h"
3 #include "msgbuf.h"
4 #include <libpldm/pdr.h>
5 #include <libpldm/platform.h>
6
7 #include <assert.h>
8 #include <endian.h>
9 #include <stdint.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13
14 #define PDR_ENTITY_ASSOCIATION_MIN_SIZE \
15 (sizeof(struct pldm_pdr_hdr) + \
16 sizeof(struct pldm_pdr_entity_association))
17
18 #define PDR_FRU_RECORD_SET_MIN_SIZE \
19 (sizeof(struct pldm_pdr_hdr) + sizeof(struct pldm_pdr_fru_record_set))
20
21 typedef struct pldm_pdr_record {
22 uint32_t record_handle;
23 uint32_t size;
24 uint8_t *data;
25 struct pldm_pdr_record *next;
26 bool is_remote;
27 uint16_t terminus_handle;
28 } pldm_pdr_record;
29
30 typedef struct pldm_pdr {
31 uint32_t record_count;
32 uint32_t size;
33 pldm_pdr_record *first;
34 pldm_pdr_record *last;
35 } pldm_pdr;
36
37 LIBPLDM_CC_NONNULL
38 static pldm_pdr_record *pldm_pdr_get_prev_record(pldm_pdr *repo,
39 pldm_pdr_record *record);
40
41 LIBPLDM_CC_NONNULL_ARGS(1, 2)
42 static int pldm_pdr_remove_record(pldm_pdr *repo, pldm_pdr_record *record,
43 pldm_pdr_record *prev);
44
45 LIBPLDM_CC_NONNULL
get_next_record_handle(const pldm_pdr * repo,const pldm_pdr_record * record)46 static inline uint32_t get_next_record_handle(const pldm_pdr *repo,
47 const pldm_pdr_record *record)
48 {
49 if (record == repo->last) {
50 return 0;
51 }
52 return record->next->record_handle;
53 }
54
55 LIBPLDM_ABI_STABLE
pldm_pdr_add(pldm_pdr * repo,const uint8_t * data,uint32_t size,bool is_remote,uint16_t terminus_handle,uint32_t * record_handle)56 int pldm_pdr_add(pldm_pdr *repo, const uint8_t *data, uint32_t size,
57 bool is_remote, uint16_t terminus_handle,
58 uint32_t *record_handle)
59 {
60 uint32_t curr = 0;
61
62 if (!repo || !data || !size) {
63 return -EINVAL;
64 }
65
66 if (record_handle && *record_handle) {
67 curr = *record_handle;
68 } else if (repo->last) {
69 curr = repo->last->record_handle;
70 if (curr == UINT32_MAX) {
71 return -EOVERFLOW;
72 }
73 curr += 1;
74 } else {
75 curr = 1;
76 }
77
78 pldm_pdr_record *record = malloc(sizeof(pldm_pdr_record));
79 if (!record) {
80 return -ENOMEM;
81 }
82
83 if (data) {
84 record->data = malloc(size);
85 if (!record->data) {
86 free(record);
87 return -ENOMEM;
88 }
89 memcpy(record->data, data, size);
90 }
91
92 record->size = size;
93 record->is_remote = is_remote;
94 record->terminus_handle = terminus_handle;
95 record->record_handle = curr;
96
97 if (record_handle && !*record_handle && data) {
98 /* If record handle is 0, that is an indication for this API to
99 * compute a new handle. For that reason, the computed handle
100 * needs to be populated in the PDR header. For a case where the
101 * caller supplied the record handle, it would exist in the
102 * header already.
103 */
104 struct pldm_pdr_hdr *hdr = (void *)record->data;
105 hdr->record_handle = htole32(record->record_handle);
106 }
107
108 record->next = NULL;
109
110 assert(!repo->first == !repo->last);
111 if (repo->first == NULL) {
112 repo->first = record;
113 repo->last = record;
114 } else {
115 repo->last->next = record;
116 repo->last = record;
117 }
118
119 repo->size += record->size;
120 ++repo->record_count;
121
122 if (record_handle) {
123 *record_handle = record->record_handle;
124 }
125
126 return 0;
127 }
128
129 LIBPLDM_ABI_STABLE
pldm_pdr_init(void)130 pldm_pdr *pldm_pdr_init(void)
131 {
132 pldm_pdr *repo = malloc(sizeof(pldm_pdr));
133 if (!repo) {
134 return NULL;
135 }
136 repo->record_count = 0;
137 repo->size = 0;
138 repo->first = NULL;
139 repo->last = NULL;
140
141 return repo;
142 }
143
144 LIBPLDM_ABI_STABLE
pldm_pdr_destroy(pldm_pdr * repo)145 void pldm_pdr_destroy(pldm_pdr *repo)
146 {
147 if (!repo) {
148 return;
149 }
150
151 pldm_pdr_record *record = repo->first;
152 while (record != NULL) {
153 pldm_pdr_record *next = record->next;
154 if (record->data) {
155 free(record->data);
156 record->data = NULL;
157 }
158 free(record);
159 record = next;
160 }
161 free(repo);
162 }
163
164 LIBPLDM_ABI_STABLE
pldm_pdr_find_record(const pldm_pdr * repo,uint32_t record_handle,uint8_t ** data,uint32_t * size,uint32_t * next_record_handle)165 const pldm_pdr_record *pldm_pdr_find_record(const pldm_pdr *repo,
166 uint32_t record_handle,
167 uint8_t **data, uint32_t *size,
168 uint32_t *next_record_handle)
169 {
170 if (!repo || !data || !size || !next_record_handle) {
171 return NULL;
172 }
173
174 if (!record_handle && (repo->first != NULL)) {
175 record_handle = repo->first->record_handle;
176 }
177
178 pldm_pdr_record *record = repo->first;
179 while (record != NULL) {
180 if (record->record_handle == record_handle) {
181 *size = record->size;
182 *data = record->data;
183 *next_record_handle =
184 get_next_record_handle(repo, record);
185 return record;
186 }
187 record = record->next;
188 }
189
190 *size = 0;
191 *next_record_handle = 0;
192 return NULL;
193 }
194
195 LIBPLDM_ABI_STABLE
196 const pldm_pdr_record *
pldm_pdr_get_next_record(const pldm_pdr * repo,const pldm_pdr_record * curr_record,uint8_t ** data,uint32_t * size,uint32_t * next_record_handle)197 pldm_pdr_get_next_record(const pldm_pdr *repo,
198 const pldm_pdr_record *curr_record, uint8_t **data,
199 uint32_t *size, uint32_t *next_record_handle)
200 {
201 if (!repo || !curr_record || !data || !size || !next_record_handle) {
202 return NULL;
203 }
204
205 if (curr_record == repo->last) {
206 *data = NULL;
207 *size = 0;
208 *next_record_handle = get_next_record_handle(repo, curr_record);
209 return NULL;
210 }
211
212 *next_record_handle = get_next_record_handle(repo, curr_record->next);
213 *data = curr_record->next->data;
214 *size = curr_record->next->size;
215 return curr_record->next;
216 }
217
218 LIBPLDM_ABI_STABLE
219 const pldm_pdr_record *
pldm_pdr_find_record_by_type(const pldm_pdr * repo,uint8_t pdr_type,const pldm_pdr_record * curr_record,uint8_t ** data,uint32_t * size)220 pldm_pdr_find_record_by_type(const pldm_pdr *repo, uint8_t pdr_type,
221 const pldm_pdr_record *curr_record, uint8_t **data,
222 uint32_t *size)
223 {
224 if (!repo) {
225 return NULL;
226 }
227
228 pldm_pdr_record *record = repo->first;
229 if (curr_record != NULL) {
230 record = curr_record->next;
231 }
232 while (record != NULL) {
233 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)record->data;
234 if (hdr->type == pdr_type) {
235 if (data && size) {
236 *size = record->size;
237 *data = record->data;
238 }
239 return record;
240 }
241 record = record->next;
242 }
243
244 if (size) {
245 *size = 0;
246 }
247 return NULL;
248 }
249
250 LIBPLDM_ABI_STABLE
pldm_pdr_get_record_count(const pldm_pdr * repo)251 uint32_t pldm_pdr_get_record_count(const pldm_pdr *repo)
252 {
253 assert(repo != NULL);
254
255 return repo->record_count;
256 }
257
258 LIBPLDM_ABI_STABLE
pldm_pdr_get_repo_size(const pldm_pdr * repo)259 uint32_t pldm_pdr_get_repo_size(const pldm_pdr *repo)
260 {
261 assert(repo != NULL);
262
263 return repo->size;
264 }
265
266 LIBPLDM_ABI_STABLE
pldm_pdr_get_record_handle(const pldm_pdr * repo LIBPLDM_CC_UNUSED,const pldm_pdr_record * record)267 uint32_t pldm_pdr_get_record_handle(const pldm_pdr *repo LIBPLDM_CC_UNUSED,
268 const pldm_pdr_record *record)
269 {
270 assert(repo != NULL);
271 assert(record != NULL);
272
273 return record->record_handle;
274 }
275
276 LIBPLDM_ABI_TESTING
pldm_pdr_get_terminus_handle(const pldm_pdr * repo LIBPLDM_CC_UNUSED,const pldm_pdr_record * record)277 uint16_t pldm_pdr_get_terminus_handle(const pldm_pdr *repo LIBPLDM_CC_UNUSED,
278 const pldm_pdr_record *record)
279 {
280 assert(repo != NULL);
281 assert(record != NULL);
282
283 return record->terminus_handle;
284 }
285
286 LIBPLDM_ABI_STABLE
pldm_pdr_record_is_remote(const pldm_pdr_record * record)287 bool pldm_pdr_record_is_remote(const pldm_pdr_record *record)
288 {
289 assert(record != NULL);
290
291 return record->is_remote;
292 }
293
294 LIBPLDM_ABI_STABLE
pldm_pdr_add_fru_record_set(pldm_pdr * repo,uint16_t terminus_handle,uint16_t fru_rsi,uint16_t entity_type,uint16_t entity_instance_num,uint16_t container_id,uint32_t * bmc_record_handle)295 int pldm_pdr_add_fru_record_set(pldm_pdr *repo, uint16_t terminus_handle,
296 uint16_t fru_rsi, uint16_t entity_type,
297 uint16_t entity_instance_num,
298 uint16_t container_id,
299 uint32_t *bmc_record_handle)
300 {
301 if (!repo || !bmc_record_handle) {
302 return -EINVAL;
303 }
304
305 uint8_t data[sizeof(struct pldm_pdr_hdr) +
306 sizeof(struct pldm_pdr_fru_record_set)];
307
308 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)&data;
309 hdr->version = 1;
310 hdr->record_handle = *bmc_record_handle;
311 hdr->type = PLDM_PDR_FRU_RECORD_SET;
312 hdr->record_change_num = 0;
313 hdr->length = htole16(sizeof(struct pldm_pdr_fru_record_set));
314 struct pldm_pdr_fru_record_set *fru =
315 (struct pldm_pdr_fru_record_set *)((uint8_t *)hdr +
316 sizeof(struct pldm_pdr_hdr));
317 fru->terminus_handle = htole16(terminus_handle);
318 fru->fru_rsi = htole16(fru_rsi);
319 fru->entity_type = htole16(entity_type);
320 fru->entity_instance_num = htole16(entity_instance_num);
321 fru->container_id = htole16(container_id);
322
323 return pldm_pdr_add(repo, data, sizeof(data), false, terminus_handle,
324 bmc_record_handle);
325 }
326
327 LIBPLDM_ABI_STABLE
pldm_pdr_fru_record_set_find_by_rsi(const pldm_pdr * repo,uint16_t fru_rsi,uint16_t * terminus_handle,uint16_t * entity_type,uint16_t * entity_instance_num,uint16_t * container_id)328 const pldm_pdr_record *pldm_pdr_fru_record_set_find_by_rsi(
329 const pldm_pdr *repo, uint16_t fru_rsi, uint16_t *terminus_handle,
330 uint16_t *entity_type, uint16_t *entity_instance_num,
331 uint16_t *container_id)
332 {
333 if (!repo || !terminus_handle || !entity_type || !entity_instance_num ||
334 !container_id) {
335 return NULL;
336 }
337
338 uint8_t *data = NULL;
339 uint32_t size = 0;
340 const pldm_pdr_record *curr_record = pldm_pdr_find_record_by_type(
341 repo, PLDM_PDR_FRU_RECORD_SET, NULL, &data, &size);
342 while (curr_record != NULL) {
343 struct pldm_pdr_fru_record_set *fru =
344 (struct pldm_pdr_fru_record_set
345 *)(data + sizeof(struct pldm_pdr_hdr));
346 if (fru->fru_rsi == htole16(fru_rsi)) {
347 *terminus_handle = le16toh(fru->terminus_handle);
348 *entity_type = le16toh(fru->entity_type);
349 *entity_instance_num =
350 le16toh(fru->entity_instance_num);
351 *container_id = le16toh(fru->container_id);
352 return curr_record;
353 }
354 data = NULL;
355 curr_record = pldm_pdr_find_record_by_type(
356 repo, PLDM_PDR_FRU_RECORD_SET, curr_record, &data,
357 &size);
358 }
359
360 *terminus_handle = 0;
361 *entity_type = 0;
362 *entity_instance_num = 0;
363 *container_id = 0;
364
365 return NULL;
366 }
367
368 LIBPLDM_ABI_STABLE
369 /* NOLINTNEXTLINE(readability-identifier-naming) */
pldm_pdr_update_TL_pdr(const pldm_pdr * repo,uint16_t terminus_handle,uint8_t tid,uint8_t tl_eid,bool valid_bit)370 void pldm_pdr_update_TL_pdr(const pldm_pdr *repo, uint16_t terminus_handle,
371 uint8_t tid, uint8_t tl_eid, bool valid_bit)
372 {
373 uint8_t *out_data = NULL;
374 uint32_t size = 0;
375 const pldm_pdr_record *record;
376 record = pldm_pdr_find_record_by_type(repo, PLDM_TERMINUS_LOCATOR_PDR,
377 NULL, &out_data, &size);
378
379 do {
380 if (record != NULL) {
381 struct pldm_terminus_locator_pdr *pdr =
382 (struct pldm_terminus_locator_pdr *)out_data;
383 struct pldm_terminus_locator_type_mctp_eid *value =
384 (struct pldm_terminus_locator_type_mctp_eid *)
385 pdr->terminus_locator_value;
386 if (pdr->terminus_handle == terminus_handle &&
387 pdr->tid == tid && value->eid == tl_eid) {
388 pdr->validity = valid_bit;
389 break;
390 }
391 }
392 record = pldm_pdr_find_record_by_type(repo,
393 PLDM_TERMINUS_LOCATOR_PDR,
394 record, &out_data, &size);
395 } while (record);
396 }
397
pldm_record_handle_in_range(uint32_t record_handle,uint32_t first_record_handle,uint32_t last_record_handle)398 static bool pldm_record_handle_in_range(uint32_t record_handle,
399 uint32_t first_record_handle,
400 uint32_t last_record_handle)
401 {
402 return record_handle >= first_record_handle &&
403 record_handle <= last_record_handle;
404 }
405
406 LIBPLDM_ABI_TESTING
pldm_pdr_find_child_container_id_index_range_exclude(const pldm_pdr * repo,uint16_t entity_type,uint16_t entity_instance,uint8_t child_index,uint32_t range_exclude_start_handle,uint32_t range_exclude_end_handle,uint16_t * container_id)407 int pldm_pdr_find_child_container_id_index_range_exclude(
408 const pldm_pdr *repo, uint16_t entity_type, uint16_t entity_instance,
409 uint8_t child_index, uint32_t range_exclude_start_handle,
410 uint32_t range_exclude_end_handle, uint16_t *container_id)
411 {
412 pldm_pdr_record *record;
413 if (!repo) {
414 return -EINVAL;
415 }
416
417 for (record = repo->first; record; record = record->next) {
418 bool is_container_entity_instance_number;
419 struct pldm_pdr_entity_association *pdr;
420 bool is_container_entity_type;
421 struct pldm_entity *child;
422 struct pldm_pdr_hdr *hdr;
423 bool in_range;
424
425 // pldm_pdr_add() takes only uint8_t* data as an argument.
426 // The expectation here is the pldm_pdr_hdr is the first field of the record data
427 hdr = (struct pldm_pdr_hdr *)record->data;
428 if (hdr->type != PLDM_PDR_ENTITY_ASSOCIATION) {
429 continue;
430 }
431 in_range = pldm_record_handle_in_range(
432 record->record_handle, range_exclude_start_handle,
433 range_exclude_end_handle);
434 if (in_range) {
435 continue;
436 }
437
438 // this cast is valid with respect to alignment because
439 // struct pldm_pdr_hdr is declared with __attribute__((packed))
440 pdr = (void *)(record->data + sizeof(struct pldm_pdr_hdr));
441 if (child_index >= pdr->num_children) {
442 continue;
443 }
444
445 child = (&pdr->children[child_index]);
446 is_container_entity_type = pdr->container.entity_type ==
447 entity_type;
448 is_container_entity_instance_number =
449 pdr->container.entity_instance_num == entity_instance;
450 if (is_container_entity_type &&
451 is_container_entity_instance_number) {
452 *container_id = le16toh(child->entity_container_id);
453 return 0;
454 }
455 }
456 return -ENOENT;
457 }
458
459 typedef struct pldm_entity_association_tree {
460 pldm_entity_node *root;
461 uint16_t last_used_container_id;
462 } pldm_entity_association_tree;
463
464 typedef struct pldm_entity_node {
465 pldm_entity entity;
466 pldm_entity parent;
467 uint16_t remote_container_id;
468 pldm_entity_node *first_child;
469 pldm_entity_node *next_sibling;
470 uint8_t association_type;
471 } pldm_entity_node;
472
473 LIBPLDM_ABI_STABLE
pldm_entity_extract(pldm_entity_node * node)474 pldm_entity pldm_entity_extract(pldm_entity_node *node)
475 {
476 assert(node != NULL);
477
478 return node->entity;
479 }
480
481 LIBPLDM_ABI_STABLE
482 uint16_t
pldm_entity_node_get_remote_container_id(const pldm_entity_node * entity)483 pldm_entity_node_get_remote_container_id(const pldm_entity_node *entity)
484 {
485 assert(entity != NULL);
486
487 return entity->remote_container_id;
488 }
489
490 LIBPLDM_ABI_STABLE
pldm_entity_association_tree_init(void)491 pldm_entity_association_tree *pldm_entity_association_tree_init(void)
492 {
493 pldm_entity_association_tree *tree =
494 malloc(sizeof(pldm_entity_association_tree));
495 if (!tree) {
496 return NULL;
497 }
498 tree->root = NULL;
499 tree->last_used_container_id = 0;
500
501 return tree;
502 }
503
504 LIBPLDM_CC_NONNULL
find_insertion_at(pldm_entity_node * start,uint16_t entity_type)505 static pldm_entity_node *find_insertion_at(pldm_entity_node *start,
506 uint16_t entity_type)
507 {
508 /* Insert after the the last node that matches the input entity type, or
509 * at the end if no such match occurs
510 */
511 while (start->next_sibling != NULL) {
512 uint16_t this_type = start->entity.entity_type;
513 pldm_entity_node *next = start->next_sibling;
514 if (this_type == entity_type &&
515 (this_type != next->entity.entity_type)) {
516 break;
517 }
518 start = start->next_sibling;
519 }
520
521 return start;
522 }
523
524 LIBPLDM_ABI_STABLE
pldm_entity_association_tree_add(pldm_entity_association_tree * tree,pldm_entity * entity,uint16_t entity_instance_number,pldm_entity_node * parent,uint8_t association_type)525 pldm_entity_node *pldm_entity_association_tree_add(
526 pldm_entity_association_tree *tree, pldm_entity *entity,
527 uint16_t entity_instance_number, pldm_entity_node *parent,
528 uint8_t association_type)
529 {
530 return pldm_entity_association_tree_add_entity(tree, entity,
531 entity_instance_number,
532 parent, association_type,
533 false, true, 0xffff);
534 }
535
536 LIBPLDM_ABI_STABLE
pldm_entity_association_tree_add_entity(pldm_entity_association_tree * tree,pldm_entity * entity,uint16_t entity_instance_number,pldm_entity_node * parent,uint8_t association_type,bool is_remote,bool is_update_container_id,uint16_t container_id)537 pldm_entity_node *pldm_entity_association_tree_add_entity(
538 pldm_entity_association_tree *tree, pldm_entity *entity,
539 uint16_t entity_instance_number, pldm_entity_node *parent,
540 uint8_t association_type, bool is_remote, bool is_update_container_id,
541 uint16_t container_id)
542 {
543 if ((!tree) || (!entity)) {
544 return NULL;
545 }
546
547 if (entity_instance_number != 0xffff && parent != NULL) {
548 pldm_entity node;
549 node.entity_type = entity->entity_type;
550 node.entity_instance_num = entity_instance_number;
551 if (pldm_is_current_parent_child(parent, &node)) {
552 return NULL;
553 }
554 }
555 if (association_type != PLDM_ENTITY_ASSOCIAION_PHYSICAL &&
556 association_type != PLDM_ENTITY_ASSOCIAION_LOGICAL) {
557 return NULL;
558 }
559 pldm_entity_node *node = malloc(sizeof(pldm_entity_node));
560 if (!node) {
561 return NULL;
562 }
563 node->first_child = NULL;
564 node->next_sibling = NULL;
565 node->parent.entity_type = 0;
566 node->parent.entity_instance_num = 0;
567 node->parent.entity_container_id = 0;
568 node->entity.entity_type = entity->entity_type;
569 node->entity.entity_instance_num =
570 entity_instance_number != 0xffff ? entity_instance_number : 1;
571 node->association_type = association_type;
572 node->remote_container_id = 0;
573 if (tree->root == NULL) {
574 if (parent != NULL) {
575 free(node);
576 return NULL;
577 }
578 tree->root = node;
579 /* container_id 0 here indicates this is the top-most entry */
580 node->entity.entity_container_id = 0;
581 node->remote_container_id = node->entity.entity_container_id;
582 } else if (parent != NULL && parent->first_child == NULL) {
583 /* Ensure next_container_id() will yield a valid ID */
584 if (tree->last_used_container_id == UINT16_MAX) {
585 free(node);
586 return NULL;
587 }
588
589 parent->first_child = node;
590 node->parent = parent->entity;
591
592 if (is_remote) {
593 node->remote_container_id = entity->entity_container_id;
594 }
595 if (is_update_container_id) {
596 if (container_id != 0xffff) {
597 node->entity.entity_container_id = container_id;
598 } else {
599 /* We will have returned above */
600 assert(tree->last_used_container_id !=
601 UINT16_MAX);
602 node->entity.entity_container_id =
603 ++tree->last_used_container_id;
604 }
605 } else {
606 node->entity.entity_container_id =
607 entity->entity_container_id;
608 }
609
610 if (!is_remote) {
611 node->remote_container_id =
612 node->entity.entity_container_id;
613 }
614 } else {
615 pldm_entity_node *start = parent == NULL ? tree->root :
616 parent->first_child;
617 pldm_entity_node *prev =
618 find_insertion_at(start, entity->entity_type);
619 if (!prev) {
620 free(node);
621 return NULL;
622 }
623 pldm_entity_node *next = prev->next_sibling;
624 if (prev->entity.entity_type == entity->entity_type) {
625 if (prev->entity.entity_instance_num == UINT16_MAX) {
626 free(node);
627 return NULL;
628 }
629 node->entity.entity_instance_num =
630 entity_instance_number != 0xffff ?
631 entity_instance_number :
632 prev->entity.entity_instance_num + 1;
633 }
634 prev->next_sibling = node;
635 node->parent = prev->parent;
636 node->next_sibling = next;
637 node->entity.entity_container_id =
638 prev->entity.entity_container_id;
639 node->remote_container_id = entity->entity_container_id;
640 }
641 entity->entity_instance_num = node->entity.entity_instance_num;
642 if (is_update_container_id) {
643 entity->entity_container_id = node->entity.entity_container_id;
644 }
645 return node;
646 }
647
get_num_nodes(pldm_entity_node * node,size_t * num)648 static void get_num_nodes(pldm_entity_node *node, size_t *num)
649 {
650 if (node == NULL) {
651 return;
652 }
653
654 ++(*num);
655 get_num_nodes(node->next_sibling, num);
656 get_num_nodes(node->first_child, num);
657 }
658
entity_association_tree_visit(pldm_entity_node * node,pldm_entity * entities,size_t * index)659 static void entity_association_tree_visit(pldm_entity_node *node,
660 pldm_entity *entities, size_t *index)
661 {
662 if (node == NULL) {
663 return;
664 }
665
666 pldm_entity *entity = &entities[*index];
667 ++(*index);
668 entity->entity_type = node->entity.entity_type;
669 entity->entity_instance_num = node->entity.entity_instance_num;
670 entity->entity_container_id = node->entity.entity_container_id;
671
672 entity_association_tree_visit(node->next_sibling, entities, index);
673 entity_association_tree_visit(node->first_child, entities, index);
674 }
675
676 LIBPLDM_ABI_STABLE
pldm_entity_association_tree_visit(pldm_entity_association_tree * tree,pldm_entity ** entities,size_t * size)677 void pldm_entity_association_tree_visit(pldm_entity_association_tree *tree,
678 pldm_entity **entities, size_t *size)
679 {
680 if (!tree || !entities || !size) {
681 return;
682 }
683
684 *size = 0;
685 if (tree->root == NULL) {
686 return;
687 }
688
689 get_num_nodes(tree->root, size);
690 *entities = malloc(*size * sizeof(pldm_entity));
691 if (!entities) {
692 return;
693 }
694 size_t index = 0;
695 entity_association_tree_visit(tree->root, *entities, &index);
696 }
697
entity_association_tree_destroy(pldm_entity_node * node)698 static void entity_association_tree_destroy(pldm_entity_node *node)
699 {
700 if (node == NULL) {
701 return;
702 }
703
704 entity_association_tree_destroy(node->next_sibling);
705 entity_association_tree_destroy(node->first_child);
706 free(node);
707 }
708
709 LIBPLDM_ABI_STABLE
pldm_entity_association_tree_destroy(pldm_entity_association_tree * tree)710 void pldm_entity_association_tree_destroy(pldm_entity_association_tree *tree)
711 {
712 if (!tree) {
713 return;
714 }
715
716 entity_association_tree_destroy(tree->root);
717 free(tree);
718 }
719
720 LIBPLDM_ABI_STABLE
pldm_entity_is_node_parent(pldm_entity_node * node)721 bool pldm_entity_is_node_parent(pldm_entity_node *node)
722 {
723 assert(node != NULL);
724
725 return node->first_child != NULL;
726 }
727
728 LIBPLDM_ABI_STABLE
pldm_entity_get_parent(pldm_entity_node * node)729 pldm_entity pldm_entity_get_parent(pldm_entity_node *node)
730 {
731 assert(node != NULL);
732
733 return node->parent;
734 }
735
736 LIBPLDM_ABI_STABLE
pldm_entity_is_exist_parent(pldm_entity_node * node)737 bool pldm_entity_is_exist_parent(pldm_entity_node *node)
738 {
739 if (!node) {
740 return false;
741 }
742
743 if (node->parent.entity_type == 0 &&
744 node->parent.entity_instance_num == 0 &&
745 node->parent.entity_container_id == 0) {
746 return false;
747 }
748
749 return true;
750 }
751
752 LIBPLDM_ABI_STABLE
pldm_entity_get_num_children(pldm_entity_node * node,uint8_t association_type)753 uint8_t pldm_entity_get_num_children(pldm_entity_node *node,
754 uint8_t association_type)
755 {
756 if (!node) {
757 return 0;
758 }
759
760 if (!(association_type == PLDM_ENTITY_ASSOCIAION_PHYSICAL ||
761 association_type == PLDM_ENTITY_ASSOCIAION_LOGICAL)) {
762 return 0;
763 }
764
765 size_t count = 0;
766 pldm_entity_node *curr = node->first_child;
767 while (curr != NULL) {
768 if (curr->association_type == association_type) {
769 ++count;
770 }
771 curr = curr->next_sibling;
772 }
773
774 assert(count < UINT8_MAX);
775 return count < UINT8_MAX ? count : 0;
776 }
777
778 LIBPLDM_ABI_STABLE
pldm_is_current_parent_child(pldm_entity_node * parent,pldm_entity * node)779 bool pldm_is_current_parent_child(pldm_entity_node *parent, pldm_entity *node)
780 {
781 if (!parent || !node) {
782 return false;
783 }
784
785 pldm_entity_node *curr = parent->first_child;
786 while (curr != NULL) {
787 if (node->entity_type == curr->entity.entity_type &&
788 node->entity_instance_num ==
789 curr->entity.entity_instance_num) {
790 return true;
791 }
792 curr = curr->next_sibling;
793 }
794
795 return false;
796 }
797
entity_association_pdr_add_children(pldm_entity_node * curr,pldm_pdr * repo,uint16_t size,uint8_t contained_count,uint8_t association_type,bool is_remote,uint16_t terminus_handle,uint32_t record_handle)798 static int entity_association_pdr_add_children(
799 pldm_entity_node *curr, pldm_pdr *repo, uint16_t size,
800 uint8_t contained_count, uint8_t association_type, bool is_remote,
801 uint16_t terminus_handle, uint32_t record_handle)
802 {
803 uint8_t *start;
804 uint8_t *pdr;
805 int rc;
806
807 pdr = calloc(1, size);
808 if (!pdr) {
809 return -ENOMEM;
810 }
811
812 start = pdr;
813
814 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)start;
815 hdr->version = 1;
816 hdr->record_handle = record_handle;
817 hdr->type = PLDM_PDR_ENTITY_ASSOCIATION;
818 hdr->record_change_num = 0;
819 hdr->length = htole16(size - sizeof(struct pldm_pdr_hdr));
820 start += sizeof(struct pldm_pdr_hdr);
821
822 uint16_t *container_id = (uint16_t *)start;
823 *container_id = htole16(curr->first_child->entity.entity_container_id);
824 start += sizeof(uint16_t);
825 *start = association_type;
826 start += sizeof(uint8_t);
827
828 pldm_entity *entity = (pldm_entity *)start;
829 entity->entity_type = htole16(curr->entity.entity_type);
830 entity->entity_instance_num = htole16(curr->entity.entity_instance_num);
831 entity->entity_container_id = htole16(curr->entity.entity_container_id);
832 start += sizeof(pldm_entity);
833
834 *start = contained_count;
835 start += sizeof(uint8_t);
836
837 pldm_entity_node *node = curr->first_child;
838 while (node != NULL) {
839 if (node->association_type == association_type) {
840 pldm_entity *entity = (pldm_entity *)start;
841 entity->entity_type = htole16(node->entity.entity_type);
842 entity->entity_instance_num =
843 htole16(node->entity.entity_instance_num);
844 entity->entity_container_id =
845 htole16(node->entity.entity_container_id);
846 start += sizeof(pldm_entity);
847 }
848 node = node->next_sibling;
849 }
850
851 rc = pldm_pdr_add(repo, pdr, size, is_remote, terminus_handle,
852 &record_handle);
853 free(pdr);
854 return rc;
855 }
856
entity_association_pdr_add_entry(pldm_entity_node * curr,pldm_pdr * repo,bool is_remote,uint16_t terminus_handle,uint32_t record_handle)857 static int entity_association_pdr_add_entry(pldm_entity_node *curr,
858 pldm_pdr *repo, bool is_remote,
859 uint16_t terminus_handle,
860 uint32_t record_handle)
861 {
862 uint8_t num_logical_children = pldm_entity_get_num_children(
863 curr, PLDM_ENTITY_ASSOCIAION_LOGICAL);
864 uint8_t num_physical_children = pldm_entity_get_num_children(
865 curr, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
866 int rc;
867
868 if (num_logical_children) {
869 uint16_t logical_pdr_size =
870 sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
871 sizeof(uint8_t) + sizeof(pldm_entity) +
872 sizeof(uint8_t) +
873 (num_logical_children * sizeof(pldm_entity));
874 rc = entity_association_pdr_add_children(
875 curr, repo, logical_pdr_size, num_logical_children,
876 PLDM_ENTITY_ASSOCIAION_LOGICAL, is_remote,
877 terminus_handle, record_handle);
878 if (rc < 0) {
879 return rc;
880 }
881 }
882
883 if (num_physical_children) {
884 uint16_t physical_pdr_size =
885 sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
886 sizeof(uint8_t) + sizeof(pldm_entity) +
887 sizeof(uint8_t) +
888 (num_physical_children * sizeof(pldm_entity));
889 rc = entity_association_pdr_add_children(
890 curr, repo, physical_pdr_size, num_physical_children,
891 PLDM_ENTITY_ASSOCIAION_PHYSICAL, is_remote,
892 terminus_handle, record_handle);
893 if (rc < 0) {
894 return rc;
895 }
896 }
897
898 return 0;
899 }
900
is_present(pldm_entity entity,pldm_entity ** entities,size_t num_entities)901 static bool is_present(pldm_entity entity, pldm_entity **entities,
902 size_t num_entities)
903 {
904 if (entities == NULL || num_entities == 0) {
905 return true;
906 }
907 size_t i = 0;
908 while (i < num_entities) {
909 if ((*entities + i)->entity_type == entity.entity_type) {
910 return true;
911 }
912 i++;
913 }
914 return false;
915 }
916
entity_association_pdr_add(pldm_entity_node * curr,pldm_pdr * repo,pldm_entity ** entities,size_t num_entities,bool is_remote,uint16_t terminus_handle,uint32_t record_handle)917 static int entity_association_pdr_add(pldm_entity_node *curr, pldm_pdr *repo,
918 pldm_entity **entities,
919 size_t num_entities, bool is_remote,
920 uint16_t terminus_handle,
921 uint32_t record_handle)
922 {
923 int rc;
924
925 if (curr == NULL) {
926 return 0;
927 }
928
929 if (is_present(curr->entity, entities, num_entities)) {
930 rc = entity_association_pdr_add_entry(
931 curr, repo, is_remote, terminus_handle, record_handle);
932 if (rc) {
933 return rc;
934 }
935 }
936
937 rc = entity_association_pdr_add(curr->next_sibling, repo, entities,
938 num_entities, is_remote,
939 terminus_handle, record_handle);
940 if (rc) {
941 return rc;
942 }
943
944 return entity_association_pdr_add(curr->first_child, repo, entities,
945 num_entities, is_remote,
946 terminus_handle, record_handle);
947 }
948
949 LIBPLDM_ABI_STABLE
pldm_entity_association_pdr_add(pldm_entity_association_tree * tree,pldm_pdr * repo,bool is_remote,uint16_t terminus_handle)950 int pldm_entity_association_pdr_add(pldm_entity_association_tree *tree,
951 pldm_pdr *repo, bool is_remote,
952 uint16_t terminus_handle)
953 {
954 if (!tree || !repo) {
955 return 0;
956 }
957
958 return entity_association_pdr_add(tree->root, repo, NULL, 0, is_remote,
959 terminus_handle, 0);
960 }
961
962 LIBPLDM_ABI_STABLE
pldm_entity_association_pdr_add_from_node(pldm_entity_node * node,pldm_pdr * repo,pldm_entity ** entities,size_t num_entities,bool is_remote,uint16_t terminus_handle)963 int pldm_entity_association_pdr_add_from_node(
964 pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
965 size_t num_entities, bool is_remote, uint16_t terminus_handle)
966 {
967 return pldm_entity_association_pdr_add_from_node_with_record_handle(
968 node, repo, entities, num_entities, is_remote, terminus_handle,
969 0);
970 }
971
972 LIBPLDM_ABI_STABLE
pldm_entity_association_pdr_add_from_node_with_record_handle(pldm_entity_node * node,pldm_pdr * repo,pldm_entity ** entities,size_t num_entities,bool is_remote,uint16_t terminus_handle,uint32_t record_handle)973 int pldm_entity_association_pdr_add_from_node_with_record_handle(
974 pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
975 size_t num_entities, bool is_remote, uint16_t terminus_handle,
976 uint32_t record_handle)
977 {
978 if (!node || !repo || !entities) {
979 return -EINVAL;
980 }
981
982 return entity_association_pdr_add(node, repo, entities, num_entities,
983 is_remote, terminus_handle,
984 record_handle);
985 }
986
find_entity_ref_in_tree(pldm_entity_node * tree_node,pldm_entity entity,pldm_entity_node ** node)987 static void find_entity_ref_in_tree(pldm_entity_node *tree_node,
988 pldm_entity entity, pldm_entity_node **node)
989 {
990 bool is_entity_container_id;
991 bool is_entity_instance_num;
992 bool is_type;
993
994 if (tree_node == NULL) {
995 return;
996 }
997
998 is_type = tree_node->entity.entity_type == entity.entity_type;
999 is_entity_instance_num = tree_node->entity.entity_instance_num ==
1000 entity.entity_instance_num;
1001 is_entity_container_id = tree_node->entity.entity_container_id ==
1002 entity.entity_container_id;
1003
1004 if (is_type && is_entity_instance_num && is_entity_container_id) {
1005 *node = tree_node;
1006 return;
1007 }
1008
1009 find_entity_ref_in_tree(tree_node->first_child, entity, node);
1010 find_entity_ref_in_tree(tree_node->next_sibling, entity, node);
1011 }
1012
1013 LIBPLDM_ABI_STABLE
pldm_find_entity_ref_in_tree(pldm_entity_association_tree * tree,pldm_entity entity,pldm_entity_node ** node)1014 void pldm_find_entity_ref_in_tree(pldm_entity_association_tree *tree,
1015 pldm_entity entity, pldm_entity_node **node)
1016 {
1017 if (!tree || !node) {
1018 return;
1019 }
1020
1021 find_entity_ref_in_tree(tree->root, entity, node);
1022 }
1023
1024 LIBPLDM_ABI_STABLE
pldm_pdr_remove_pdrs_by_terminus_handle(pldm_pdr * repo,uint16_t terminus_handle)1025 void pldm_pdr_remove_pdrs_by_terminus_handle(pldm_pdr *repo,
1026 uint16_t terminus_handle)
1027 {
1028 if (!repo) {
1029 return;
1030 }
1031
1032 bool removed = false;
1033
1034 pldm_pdr_record *record = repo->first;
1035 pldm_pdr_record *prev = NULL;
1036 while (record != NULL) {
1037 pldm_pdr_record *next = record->next;
1038 if (record->terminus_handle == terminus_handle) {
1039 if (repo->first == record) {
1040 repo->first = next;
1041 } else {
1042 prev->next = next;
1043 }
1044 if (repo->last == record) {
1045 repo->last = prev;
1046 }
1047 if (record->data) {
1048 free(record->data);
1049 }
1050 --repo->record_count;
1051 repo->size -= record->size;
1052 free(record);
1053 removed = true;
1054 } else {
1055 prev = record;
1056 }
1057 record = next;
1058 }
1059
1060 if (removed == true) {
1061 record = repo->first;
1062 uint32_t record_handle = 0;
1063 while (record != NULL) {
1064 record->record_handle = ++record_handle;
1065 if (record->data != NULL) {
1066 struct pldm_pdr_hdr *hdr =
1067 (struct pldm_pdr_hdr *)(record->data);
1068 hdr->record_handle =
1069 htole32(record->record_handle);
1070 }
1071 record = record->next;
1072 }
1073 }
1074 }
1075
1076 LIBPLDM_ABI_STABLE
pldm_pdr_remove_remote_pdrs(pldm_pdr * repo)1077 void pldm_pdr_remove_remote_pdrs(pldm_pdr *repo)
1078 {
1079 if (!repo) {
1080 return;
1081 }
1082
1083 bool removed = false;
1084
1085 pldm_pdr_record *record = repo->first;
1086 pldm_pdr_record *prev = NULL;
1087 while (record != NULL) {
1088 pldm_pdr_record *next = record->next;
1089 if (record->is_remote == true) {
1090 if (repo->first == record) {
1091 repo->first = next;
1092 } else {
1093 prev->next = next;
1094 }
1095 if (repo->last == record) {
1096 repo->last = prev;
1097 }
1098 if (record->data) {
1099 free(record->data);
1100 }
1101 --repo->record_count;
1102 repo->size -= record->size;
1103 free(record);
1104 removed = true;
1105 } else {
1106 prev = record;
1107 }
1108 record = next;
1109 }
1110
1111 if (removed == true) {
1112 record = repo->first;
1113 uint32_t record_handle = 0;
1114 while (record != NULL) {
1115 record->record_handle = ++record_handle;
1116 if (record->data != NULL) {
1117 struct pldm_pdr_hdr *hdr =
1118 (struct pldm_pdr_hdr *)(record->data);
1119 hdr->record_handle =
1120 htole32(record->record_handle);
1121 }
1122 record = record->next;
1123 }
1124 }
1125 }
1126
1127 LIBPLDM_ABI_STABLE
pldm_pdr_find_last_in_range(const pldm_pdr * repo,uint32_t first,uint32_t last)1128 pldm_pdr_record *pldm_pdr_find_last_in_range(const pldm_pdr *repo,
1129 uint32_t first, uint32_t last)
1130 {
1131 pldm_pdr_record *record = NULL;
1132 pldm_pdr_record *curr;
1133
1134 if (!repo) {
1135 return NULL;
1136 }
1137 for (curr = repo->first; curr; curr = curr->next) {
1138 if (first > curr->record_handle || last < curr->record_handle) {
1139 continue;
1140 }
1141 if (!record || curr->record_handle > record->record_handle) {
1142 record = curr;
1143 }
1144 }
1145
1146 return record;
1147 }
1148
entity_association_tree_find_if_remote(pldm_entity_node * node,pldm_entity * entity,pldm_entity_node ** out,bool is_remote)1149 static void entity_association_tree_find_if_remote(pldm_entity_node *node,
1150 pldm_entity *entity,
1151 pldm_entity_node **out,
1152 bool is_remote)
1153 {
1154 if (node == NULL) {
1155 return;
1156 }
1157 bool is_entity_type;
1158 bool is_entity_instance_num;
1159
1160 is_entity_type = node->entity.entity_type == entity->entity_type;
1161 is_entity_instance_num = node->entity.entity_instance_num ==
1162 entity->entity_instance_num;
1163
1164 if (!is_remote ||
1165 node->remote_container_id == entity->entity_container_id) {
1166 if (is_entity_type && is_entity_instance_num) {
1167 entity->entity_container_id =
1168 node->entity.entity_container_id;
1169 *out = node;
1170 return;
1171 }
1172 }
1173 entity_association_tree_find_if_remote(node->next_sibling, entity, out,
1174 is_remote);
1175 entity_association_tree_find_if_remote(node->first_child, entity, out,
1176 is_remote);
1177 }
1178
1179 LIBPLDM_ABI_STABLE
pldm_entity_association_tree_find_with_locality(pldm_entity_association_tree * tree,pldm_entity * entity,bool is_remote)1180 pldm_entity_node *pldm_entity_association_tree_find_with_locality(
1181 pldm_entity_association_tree *tree, pldm_entity *entity, bool is_remote)
1182 {
1183 if (!tree || !entity) {
1184 return NULL;
1185 }
1186 pldm_entity_node *node = NULL;
1187 entity_association_tree_find_if_remote(tree->root, entity, &node,
1188 is_remote);
1189 return node;
1190 }
1191
entity_association_tree_find(pldm_entity_node * node,pldm_entity * entity,pldm_entity_node ** out)1192 static void entity_association_tree_find(pldm_entity_node *node,
1193 pldm_entity *entity,
1194 pldm_entity_node **out)
1195 {
1196 if (node == NULL) {
1197 return;
1198 }
1199
1200 if (node->entity.entity_type == entity->entity_type &&
1201 node->entity.entity_instance_num == entity->entity_instance_num) {
1202 entity->entity_container_id = node->entity.entity_container_id;
1203 *out = node;
1204 return;
1205 }
1206 entity_association_tree_find(node->next_sibling, entity, out);
1207 entity_association_tree_find(node->first_child, entity, out);
1208 }
1209
1210 LIBPLDM_ABI_STABLE
1211 pldm_entity_node *
pldm_entity_association_tree_find(pldm_entity_association_tree * tree,pldm_entity * entity)1212 pldm_entity_association_tree_find(pldm_entity_association_tree *tree,
1213 pldm_entity *entity)
1214 {
1215 if (!tree || !entity) {
1216 return NULL;
1217 }
1218
1219 pldm_entity_node *node = NULL;
1220 entity_association_tree_find(tree->root, entity, &node);
1221 return node;
1222 }
1223
entity_association_tree_copy(pldm_entity_node * org_node,pldm_entity_node ** new_node)1224 static int entity_association_tree_copy(pldm_entity_node *org_node,
1225 pldm_entity_node **new_node)
1226 {
1227 int rc;
1228
1229 if (org_node == NULL) {
1230 return 0;
1231 }
1232
1233 *new_node = malloc(sizeof(pldm_entity_node));
1234 if (!*new_node) {
1235 return -ENOMEM;
1236 }
1237
1238 (*new_node)->parent = org_node->parent;
1239 (*new_node)->entity = org_node->entity;
1240 (*new_node)->association_type = org_node->association_type;
1241 (*new_node)->remote_container_id = org_node->remote_container_id;
1242 (*new_node)->first_child = NULL;
1243 (*new_node)->next_sibling = NULL;
1244
1245 rc = entity_association_tree_copy(org_node->first_child,
1246 &((*new_node)->first_child));
1247 if (rc) {
1248 goto cleanup;
1249 }
1250
1251 rc = entity_association_tree_copy(org_node->next_sibling,
1252 &((*new_node)->next_sibling));
1253 if (rc) {
1254 entity_association_tree_destroy((*new_node)->first_child);
1255 goto cleanup;
1256 }
1257
1258 return 0;
1259
1260 cleanup:
1261 free(*new_node);
1262 *new_node = NULL;
1263 return rc;
1264 }
1265
1266 LIBPLDM_ABI_DEPRECATED_UNSAFE
pldm_entity_association_tree_copy_root(pldm_entity_association_tree * org_tree,pldm_entity_association_tree * new_tree)1267 void pldm_entity_association_tree_copy_root(
1268 pldm_entity_association_tree *org_tree,
1269 pldm_entity_association_tree *new_tree)
1270 {
1271 assert(org_tree != NULL);
1272 assert(new_tree != NULL);
1273
1274 new_tree->last_used_container_id = org_tree->last_used_container_id;
1275 entity_association_tree_copy(org_tree->root, &(new_tree->root));
1276 }
1277
1278 LIBPLDM_ABI_TESTING
pldm_entity_association_tree_copy_root_check(pldm_entity_association_tree * org_tree,pldm_entity_association_tree * new_tree)1279 int pldm_entity_association_tree_copy_root_check(
1280 pldm_entity_association_tree *org_tree,
1281 pldm_entity_association_tree *new_tree)
1282 {
1283 if (!org_tree || !new_tree) {
1284 return -EINVAL;
1285 }
1286
1287 new_tree->last_used_container_id = org_tree->last_used_container_id;
1288 return entity_association_tree_copy(org_tree->root, &(new_tree->root));
1289 }
1290
1291 LIBPLDM_ABI_STABLE
pldm_entity_association_tree_destroy_root(pldm_entity_association_tree * tree)1292 void pldm_entity_association_tree_destroy_root(
1293 pldm_entity_association_tree *tree)
1294 {
1295 if (!tree) {
1296 return;
1297 }
1298
1299 entity_association_tree_destroy(tree->root);
1300 tree->last_used_container_id = 0;
1301 tree->root = NULL;
1302 }
1303
1304 LIBPLDM_ABI_STABLE
pldm_is_empty_entity_assoc_tree(pldm_entity_association_tree * tree)1305 bool pldm_is_empty_entity_assoc_tree(pldm_entity_association_tree *tree)
1306 {
1307 return ((tree->root == NULL) ? true : false);
1308 }
1309
1310 LIBPLDM_ABI_STABLE
pldm_entity_association_pdr_extract(const uint8_t * pdr,uint16_t pdr_len,size_t * num_entities,pldm_entity ** entities)1311 void pldm_entity_association_pdr_extract(const uint8_t *pdr, uint16_t pdr_len,
1312 size_t *num_entities,
1313 pldm_entity **entities)
1314 {
1315 if (!pdr || !num_entities || !entities) {
1316 return;
1317 }
1318 if (pdr_len < PDR_ENTITY_ASSOCIATION_MIN_SIZE) {
1319 return;
1320 }
1321
1322 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)pdr;
1323 if (hdr->type != PLDM_PDR_ENTITY_ASSOCIATION) {
1324 return;
1325 }
1326
1327 const uint8_t *start = (uint8_t *)pdr;
1328
1329 if (UINTPTR_MAX - (uintptr_t)start <
1330 (sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length))) {
1331 return;
1332 }
1333
1334 if (pdr_len < (sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length))) {
1335 return;
1336 }
1337
1338 const uint8_t *end =
1339 start + sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length);
1340 start += sizeof(struct pldm_pdr_hdr);
1341
1342 if ((uintptr_t)end - (uintptr_t)start <
1343 sizeof(struct pldm_pdr_entity_association)) {
1344 return;
1345 }
1346 struct pldm_pdr_entity_association *entity_association_pdr =
1347 (struct pldm_pdr_entity_association *)start;
1348
1349 if (entity_association_pdr->num_children == UINT8_MAX) {
1350 return;
1351 }
1352
1353 size_t l_num_entities = entity_association_pdr->num_children + 1;
1354 if (l_num_entities < 2) {
1355 return;
1356 }
1357
1358 if (UINT8_MAX < l_num_entities) {
1359 return;
1360 }
1361
1362 pldm_entity *l_entities = calloc(l_num_entities, sizeof(pldm_entity));
1363 if (!l_entities) {
1364 return;
1365 }
1366 l_entities[0].entity_type =
1367 le16toh(entity_association_pdr->container.entity_type);
1368 l_entities[0].entity_instance_num =
1369 le16toh(entity_association_pdr->container.entity_instance_num);
1370 l_entities[0].entity_container_id =
1371 le16toh(entity_association_pdr->container.entity_container_id);
1372 pldm_entity *curr_entity = entity_association_pdr->children;
1373 for (size_t i = 1; i < l_num_entities; i++, curr_entity++) {
1374 l_entities[i].entity_type = le16toh(curr_entity->entity_type);
1375 l_entities[i].entity_instance_num =
1376 le16toh(curr_entity->entity_instance_num);
1377 l_entities[i].entity_container_id =
1378 le16toh(curr_entity->entity_container_id);
1379 }
1380
1381 *num_entities = l_num_entities;
1382 *entities = l_entities;
1383 }
1384
1385 /* Find the position of record in pldm_pdr repo and place new_record in
1386 * the same position.
1387 */
1388 LIBPLDM_CC_NONNULL
pldm_pdr_replace_record(pldm_pdr * repo,pldm_pdr_record * record,pldm_pdr_record * prev,pldm_pdr_record * new_record)1389 static int pldm_pdr_replace_record(pldm_pdr *repo, pldm_pdr_record *record,
1390 pldm_pdr_record *prev,
1391 pldm_pdr_record *new_record)
1392 {
1393 if (repo->size < record->size) {
1394 return -EOVERFLOW;
1395 }
1396
1397 if (repo->size + new_record->size < new_record->size) {
1398 return -EOVERFLOW;
1399 }
1400
1401 if (repo->first == record) {
1402 repo->first = new_record;
1403 } else {
1404 prev->next = new_record;
1405 }
1406 new_record->next = record->next;
1407
1408 if (repo->last == record) {
1409 repo->last = new_record;
1410 }
1411
1412 repo->size = (repo->size - record->size) + new_record->size;
1413 return 0;
1414 }
1415
1416 /* Insert a new record to pldm_pdr repo to a position that comes after
1417 * pldm_pdr_record record.
1418 */
1419 LIBPLDM_CC_NONNULL
pldm_pdr_insert_record(pldm_pdr * repo,pldm_pdr_record * record,pldm_pdr_record * new_record)1420 static int pldm_pdr_insert_record(pldm_pdr *repo, pldm_pdr_record *record,
1421 pldm_pdr_record *new_record)
1422 {
1423 if (repo->size + new_record->size < new_record->size) {
1424 return -EOVERFLOW;
1425 }
1426
1427 if (repo->record_count == UINT32_MAX) {
1428 return -EOVERFLOW;
1429 }
1430
1431 new_record->next = record->next;
1432 record->next = new_record;
1433
1434 if (repo->last == record) {
1435 repo->last = new_record;
1436 }
1437
1438 repo->size = repo->size + new_record->size;
1439 ++repo->record_count;
1440 return 0;
1441 }
1442
1443 /* Find the position of PDR when its record handle is known
1444 */
1445 LIBPLDM_CC_NONNULL
pldm_pdr_find_record_by_handle(pldm_pdr_record ** record,pldm_pdr_record ** prev,uint32_t record_handle)1446 static bool pldm_pdr_find_record_by_handle(pldm_pdr_record **record,
1447 pldm_pdr_record **prev,
1448 uint32_t record_handle)
1449 {
1450 while (*record != NULL) {
1451 if ((*record)->record_handle == record_handle) {
1452 return true;
1453 }
1454 *prev = *record;
1455 *record = (*record)->next;
1456 }
1457 return false;
1458 }
1459
1460 LIBPLDM_ABI_TESTING
pldm_entity_association_pdr_add_contained_entity_to_remote_pdr(pldm_pdr * repo,pldm_entity * entity,uint32_t pdr_record_handle)1461 int pldm_entity_association_pdr_add_contained_entity_to_remote_pdr(
1462 pldm_pdr *repo, pldm_entity *entity, uint32_t pdr_record_handle)
1463 {
1464 if (!repo || !entity) {
1465 return -EINVAL;
1466 }
1467
1468 pldm_pdr_record *record = repo->first;
1469 pldm_pdr_record *prev = repo->first;
1470 int rc = 0;
1471 uint16_t header_length = 0;
1472 uint8_t num_children = 0;
1473 struct pldm_msgbuf _src;
1474 struct pldm_msgbuf *src = &_src;
1475 struct pldm_msgbuf _dst;
1476 struct pldm_msgbuf *dst = &_dst;
1477
1478 pldm_pdr_find_record_by_handle(&record, &prev, pdr_record_handle);
1479
1480 if (!record) {
1481 return -EINVAL;
1482 }
1483 // Initialize msg buffer for record and record->data
1484 rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1485 record->data, record->size);
1486 if (rc) {
1487 return rc;
1488 }
1489
1490 // check if adding another entity to record causes overflow before
1491 // allocating memory for new_record.
1492 if (record->size + sizeof(pldm_entity) < sizeof(pldm_entity)) {
1493 return -EOVERFLOW;
1494 }
1495 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1496 if (!new_record) {
1497 return -ENOMEM;
1498 }
1499
1500 new_record->data = malloc(record->size + sizeof(pldm_entity));
1501 if (!new_record->data) {
1502 rc = -ENOMEM;
1503 goto cleanup_new_record;
1504 }
1505
1506 new_record->record_handle = record->record_handle;
1507 new_record->size = record->size + sizeof(struct pldm_entity);
1508 new_record->is_remote = record->is_remote;
1509
1510 // Initialize new PDR record with data from original PDR record.
1511 // Start with adding the header of original PDR
1512 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1513 new_record->data, new_record->size);
1514 if (rc) {
1515 goto cleanup_new_record_data;
1516 }
1517
1518 pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle);
1519 pldm_msgbuf_copy(dst, src, uint8_t, hdr_version);
1520 pldm_msgbuf_copy(dst, src, uint8_t, hdr_type);
1521 pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num);
1522 // extract the header length from record and increment size with
1523 // size of pldm_entity before inserting the value into new_record.
1524 rc = pldm_msgbuf_extract(src, header_length);
1525 if (rc) {
1526 goto cleanup_new_record_data;
1527 }
1528 static_assert(UINT16_MAX < (SIZE_MAX - sizeof(pldm_entity)),
1529 "Fix the following bounds check.");
1530 if (header_length + sizeof(pldm_entity) > UINT16_MAX) {
1531 rc = -EOVERFLOW;
1532 goto cleanup_new_record_data;
1533 }
1534 header_length += sizeof(pldm_entity);
1535 pldm_msgbuf_insert(dst, header_length);
1536 pldm_msgbuf_copy(dst, src, uint16_t, container_id);
1537 pldm_msgbuf_copy(dst, src, uint8_t, association_type);
1538 pldm_msgbuf_copy(dst, src, uint16_t, entity_type);
1539 pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num);
1540 pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id);
1541 // extract value of number of children from record and increment it
1542 // by 1 before insert the value to new record.
1543 rc = pldm_msgbuf_extract(src, num_children);
1544 if (rc) {
1545 goto cleanup_new_record_data;
1546 }
1547 if (num_children == UINT8_MAX) {
1548 rc = -EOVERFLOW;
1549 goto cleanup_new_record_data;
1550 }
1551 num_children += 1;
1552 pldm_msgbuf_insert(dst, num_children);
1553 //Add all children of original PDR to new PDR
1554 for (int i = 0; i < num_children - 1; i++) {
1555 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1556 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1557 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1558 }
1559
1560 // Add new contained entity as a child of new PDR
1561 rc = pldm_msgbuf_destroy(src);
1562 if (rc) {
1563 goto cleanup_new_record_data;
1564 }
1565 rc = pldm_msgbuf_init_errno(src, sizeof(struct pldm_entity), entity,
1566 sizeof(struct pldm_entity));
1567 if (rc) {
1568 goto cleanup_new_record_data;
1569 }
1570 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1571 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1572 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1573
1574 rc = pldm_msgbuf_destroy(src);
1575 if (rc) {
1576 goto cleanup_new_record_data;
1577 }
1578 rc = pldm_msgbuf_destroy(dst);
1579 if (rc) {
1580 goto cleanup_new_record_data;
1581 }
1582
1583 rc = pldm_pdr_replace_record(repo, record, prev, new_record);
1584 if (rc) {
1585 goto cleanup_new_record_data;
1586 }
1587
1588 free(record->data);
1589 free(record);
1590 return rc;
1591 cleanup_new_record_data:
1592 free(new_record->data);
1593 cleanup_new_record:
1594 free(new_record);
1595 return rc;
1596 }
1597
1598 LIBPLDM_ABI_TESTING
pldm_entity_association_pdr_create_new(pldm_pdr * repo,uint32_t pdr_record_handle,pldm_entity * parent,pldm_entity * entity,uint32_t * entity_record_handle)1599 int pldm_entity_association_pdr_create_new(pldm_pdr *repo,
1600 uint32_t pdr_record_handle,
1601 pldm_entity *parent,
1602 pldm_entity *entity,
1603 uint32_t *entity_record_handle)
1604 {
1605 if (!repo || !parent || !entity || !entity_record_handle) {
1606 return -EINVAL;
1607 }
1608
1609 if (pdr_record_handle == UINT32_MAX) {
1610 return -EOVERFLOW;
1611 }
1612
1613 bool pdr_added = false;
1614 uint16_t new_pdr_size;
1615 uint16_t container_id = 0;
1616 void *container_id_addr;
1617 struct pldm_msgbuf _dst;
1618 struct pldm_msgbuf *dst = &_dst;
1619 struct pldm_msgbuf _src_p;
1620 struct pldm_msgbuf *src_p = &_src_p;
1621 struct pldm_msgbuf _src_c;
1622 struct pldm_msgbuf *src_c = &_src_c;
1623 int rc = 0;
1624
1625 pldm_pdr_record *prev = repo->first;
1626 pldm_pdr_record *record = repo->first;
1627 pdr_added = pldm_pdr_find_record_by_handle(&record, &prev,
1628 pdr_record_handle);
1629 if (!pdr_added) {
1630 return -ENOENT;
1631 }
1632
1633 static_assert(PDR_ENTITY_ASSOCIATION_MIN_SIZE < UINT16_MAX,
1634 "Truncation ahead");
1635 new_pdr_size = PDR_ENTITY_ASSOCIATION_MIN_SIZE;
1636 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1637 if (!new_record) {
1638 return -ENOMEM;
1639 }
1640
1641 new_record->data = malloc(new_pdr_size);
1642 if (!new_record->data) {
1643 rc = -ENOMEM;
1644 goto cleanup_new_record;
1645 }
1646
1647 // Initialise new PDR to be added with the header, size and handle.
1648 // Set the position of new PDR
1649 *entity_record_handle = pdr_record_handle + 1;
1650 new_record->record_handle = *entity_record_handle;
1651 new_record->size = new_pdr_size;
1652 new_record->is_remote = false;
1653
1654 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1655 new_record->data, new_record->size);
1656 if (rc) {
1657 goto cleanup_new_record_data;
1658 }
1659
1660 // header record handle
1661 pldm_msgbuf_insert(dst, *entity_record_handle);
1662 // header version
1663 pldm_msgbuf_insert_uint8(dst, 1);
1664 // header type
1665 pldm_msgbuf_insert_uint8(dst, PLDM_PDR_ENTITY_ASSOCIATION);
1666 // header change number
1667 pldm_msgbuf_insert_uint16(dst, 0);
1668 // header length
1669 pldm_msgbuf_insert_uint16(dst,
1670 (new_pdr_size - sizeof(struct pldm_pdr_hdr)));
1671
1672 // Data for new PDR is obtained from parent PDR and new contained entity
1673 // is added as the child
1674 rc = pldm_msgbuf_init_errno(src_p, sizeof(struct pldm_entity), parent,
1675 sizeof(*parent));
1676 if (rc) {
1677 goto cleanup_new_record_data;
1678 }
1679
1680 rc = pldm_msgbuf_init_errno(src_c, sizeof(struct pldm_entity), entity,
1681 sizeof(*entity));
1682 if (rc) {
1683 goto cleanup_new_record_data;
1684 }
1685
1686 container_id_addr = NULL;
1687 // extract pointer for container ID and save the address
1688 rc = pldm_msgbuf_span_required(dst, sizeof(container_id),
1689 (void **)&container_id_addr);
1690 if (rc) {
1691 goto cleanup_new_record_data;
1692 }
1693 assert(container_id_addr);
1694 pldm_msgbuf_insert_uint8(dst, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
1695 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_type);
1696 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_instance_num);
1697 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_container_id);
1698 // number of children
1699 pldm_msgbuf_insert_uint8(dst, 1);
1700
1701 // Add new entity as child
1702 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_type);
1703 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_instance_num);
1704 // Extract and insert child entity container ID and add same value to
1705 // container ID of entity
1706 pldm_msgbuf_extract(src_c, container_id);
1707 pldm_msgbuf_insert(dst, container_id);
1708 container_id = htole16(container_id);
1709 memcpy(container_id_addr, &container_id, sizeof(uint16_t));
1710
1711 rc = pldm_msgbuf_destroy(dst);
1712 if (rc) {
1713 goto cleanup_new_record_data;
1714 }
1715 rc = pldm_msgbuf_destroy(src_p);
1716 if (rc) {
1717 goto cleanup_new_record_data;
1718 }
1719 rc = pldm_msgbuf_destroy(src_c);
1720 if (rc) {
1721 goto cleanup_new_record_data;
1722 }
1723
1724 rc = pldm_pdr_insert_record(repo, record, new_record);
1725 if (rc) {
1726 goto cleanup_new_record_data;
1727 }
1728
1729 return rc;
1730 cleanup_new_record_data:
1731 free(new_record->data);
1732 cleanup_new_record:
1733 free(new_record);
1734 return rc;
1735 }
1736
1737 LIBPLDM_CC_NONNULL
pldm_entity_cmp(const struct pldm_entity * l,const struct pldm_entity * r)1738 static bool pldm_entity_cmp(const struct pldm_entity *l,
1739 const struct pldm_entity *r)
1740 {
1741 return l->entity_type == r->entity_type &&
1742 l->entity_instance_num == r->entity_instance_num &&
1743 l->entity_container_id == r->entity_container_id;
1744 }
1745
1746 /* Find record handle of a PDR record from PDR repo and
1747 * entity
1748 */
1749 LIBPLDM_CC_NONNULL
pldm_entity_association_find_record_handle_by_entity(pldm_pdr * repo,pldm_entity * entity,bool is_remote,uint32_t * record_handle)1750 static int pldm_entity_association_find_record_handle_by_entity(
1751 pldm_pdr *repo, pldm_entity *entity, bool is_remote,
1752 uint32_t *record_handle)
1753 {
1754 uint8_t num_children = 0;
1755 uint8_t hdr_type = 0;
1756 int rc = 0;
1757 size_t skip_data_size = 0;
1758 pldm_pdr_record *record = repo->first;
1759 struct pldm_msgbuf _dst;
1760 struct pldm_msgbuf *dst = &_dst;
1761
1762 while (record != NULL) {
1763 rc = pldm_msgbuf_init_errno(dst,
1764 PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1765 record->data, record->size);
1766 if (rc) {
1767 return rc;
1768 }
1769 skip_data_size = sizeof(uint32_t) + sizeof(uint8_t);
1770 pldm_msgbuf_span_required(dst, skip_data_size, NULL);
1771 pldm_msgbuf_extract(dst, hdr_type);
1772 if (record->is_remote != is_remote ||
1773 hdr_type != PLDM_PDR_ENTITY_ASSOCIATION) {
1774 goto cleanup;
1775 }
1776 skip_data_size = sizeof(uint16_t) + sizeof(uint16_t) +
1777 sizeof(uint16_t) + sizeof(uint8_t) +
1778 sizeof(struct pldm_entity);
1779 pldm_msgbuf_span_required(dst, skip_data_size, NULL);
1780 pldm_msgbuf_extract(dst, num_children);
1781 for (int i = 0; i < num_children; ++i) {
1782 struct pldm_entity e;
1783
1784 if ((rc = pldm_msgbuf_extract(dst, e.entity_type)) ||
1785 (rc = pldm_msgbuf_extract(dst,
1786 e.entity_instance_num)) ||
1787 (rc = pldm_msgbuf_extract(dst,
1788 e.entity_container_id))) {
1789 return rc;
1790 }
1791
1792 if (pldm_entity_cmp(entity, &e)) {
1793 *record_handle = record->record_handle;
1794 return 0;
1795 }
1796 }
1797 cleanup:
1798 rc = pldm_msgbuf_destroy(dst);
1799 if (rc) {
1800 return rc;
1801 }
1802 record = record->next;
1803 }
1804 return 0;
1805 }
1806
1807 LIBPLDM_ABI_TESTING
pldm_entity_association_pdr_remove_contained_entity(pldm_pdr * repo,pldm_entity * entity,bool is_remote,uint32_t * pdr_record_handle)1808 int pldm_entity_association_pdr_remove_contained_entity(
1809 pldm_pdr *repo, pldm_entity *entity, bool is_remote,
1810 uint32_t *pdr_record_handle)
1811 {
1812 uint16_t header_length = 0;
1813 uint8_t num_children = 0;
1814 struct pldm_msgbuf _src;
1815 struct pldm_msgbuf *src = &_src;
1816 struct pldm_msgbuf _dst;
1817 struct pldm_msgbuf *dst = &_dst;
1818 int rc;
1819 pldm_pdr_record *record;
1820 pldm_pdr_record *prev;
1821
1822 if (!repo || !entity || !pdr_record_handle) {
1823 return -EINVAL;
1824 }
1825 record = repo->first;
1826 prev = repo->first;
1827
1828 rc = pldm_entity_association_find_record_handle_by_entity(
1829 repo, entity, is_remote, pdr_record_handle);
1830 if (rc) {
1831 return rc;
1832 }
1833 pldm_pdr_find_record_by_handle(&record, &prev, *pdr_record_handle);
1834 if (!record) {
1835 return -EINVAL;
1836 }
1837 // Initialize msg buffer for record and record->data
1838 rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1839 record->data, record->size);
1840 if (rc) {
1841 return rc;
1842 }
1843 // check if removing an entity from record causes overflow before
1844 // allocating memory for new_record.
1845 if (record->size < sizeof(pldm_entity)) {
1846 return -EOVERFLOW;
1847 }
1848 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1849 if (!new_record) {
1850 return -ENOMEM;
1851 }
1852 new_record->data = malloc(record->size - sizeof(pldm_entity));
1853 if (!new_record->data) {
1854 rc = -ENOMEM;
1855 goto cleanup_new_record;
1856 }
1857 new_record->record_handle = record->record_handle;
1858 new_record->size = record->size - sizeof(struct pldm_entity);
1859 new_record->is_remote = record->is_remote;
1860
1861 // Initialize new PDR record with data from original PDR record.
1862 // Start with adding the header of original PDR
1863 rc = pldm_msgbuf_init_errno(
1864 dst, (PDR_ENTITY_ASSOCIATION_MIN_SIZE - sizeof(pldm_entity)),
1865 new_record->data, new_record->size);
1866 if (rc) {
1867 goto cleanup_new_record_data;
1868 }
1869 pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle);
1870 pldm_msgbuf_copy(dst, src, uint8_t, hdr_version);
1871 pldm_msgbuf_copy(dst, src, uint8_t, hdr_type);
1872 pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num);
1873 // extract the header length from record and decrement size with
1874 // size of pldm_entity before inserting the value into new_record.
1875 rc = pldm_msgbuf_extract(src, header_length);
1876 if (rc) {
1877 goto cleanup_new_record_data;
1878 }
1879 if (header_length < sizeof(pldm_entity)) {
1880 rc = -EOVERFLOW;
1881 goto cleanup_new_record_data;
1882 }
1883 header_length -= sizeof(pldm_entity);
1884 pldm_msgbuf_insert(dst, header_length);
1885 pldm_msgbuf_copy(dst, src, uint16_t, container_id);
1886 pldm_msgbuf_copy(dst, src, uint8_t, association_type);
1887 pldm_msgbuf_copy(dst, src, uint16_t, entity_type);
1888 pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num);
1889 pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id);
1890 // extract value of number of children from record and decrement it
1891 // by 1 before insert the value to new record.
1892 rc = pldm_msgbuf_extract(src, num_children);
1893 if (rc) {
1894 goto cleanup_new_record_data;
1895 }
1896 if (num_children == 1) {
1897 // This is the last child which is getting removed so we need to delete the Entity Association PDR.
1898 pldm_pdr_remove_record(repo, record,
1899 pldm_pdr_get_prev_record(repo, record));
1900 goto cleanup_new_record_data;
1901 } else if (num_children < 1) {
1902 rc = -EOVERFLOW;
1903 goto cleanup_new_record_data;
1904 }
1905 num_children -= 1;
1906 pldm_msgbuf_insert(dst, num_children);
1907 //Add all children of original PDR to new PDR
1908 for (int i = 0; i < num_children + 1; ++i) {
1909 struct pldm_entity e;
1910
1911 if ((rc = pldm_msgbuf_extract(src, e.entity_type)) ||
1912 (rc = pldm_msgbuf_extract(src, e.entity_instance_num)) ||
1913 (rc = pldm_msgbuf_extract(src, e.entity_container_id))) {
1914 goto cleanup_new_record_data;
1915 }
1916
1917 if (pldm_entity_cmp(entity, &e)) {
1918 continue;
1919 }
1920
1921 pldm_msgbuf_insert(dst, e.entity_type);
1922 pldm_msgbuf_insert(dst, e.entity_instance_num);
1923 pldm_msgbuf_insert(dst, e.entity_container_id);
1924 }
1925
1926 if ((rc = pldm_msgbuf_destroy(src)) ||
1927 (rc = pldm_msgbuf_destroy(dst)) ||
1928 (rc = pldm_pdr_replace_record(repo, record, prev, new_record))) {
1929 goto cleanup_new_record_data;
1930 }
1931
1932 free(record->data);
1933 free(record);
1934 return rc;
1935
1936 cleanup_new_record_data:
1937 free(new_record->data);
1938 cleanup_new_record:
1939 free(new_record);
1940 return rc;
1941 }
1942
1943 /* API to find the PDR record that is previous to a given PLDM PDR
1944 * record in a given PLDM PDR repository
1945 */
1946 LIBPLDM_CC_NONNULL
pldm_pdr_get_prev_record(pldm_pdr * repo,pldm_pdr_record * record)1947 static pldm_pdr_record *pldm_pdr_get_prev_record(pldm_pdr *repo,
1948 pldm_pdr_record *record)
1949 {
1950 pldm_pdr_record *prev = NULL;
1951 pldm_pdr_record *curr = repo->first;
1952
1953 while (curr != NULL) {
1954 if (curr->record_handle == record->record_handle) {
1955 break;
1956 }
1957 prev = curr;
1958 curr = curr->next;
1959 }
1960 return prev;
1961 }
1962
1963 /* API to check if a PLDM PDR record is present in a PLDM PDR repository
1964 */
1965 LIBPLDM_CC_NONNULL
is_prev_record_present(pldm_pdr * repo,pldm_pdr_record * record)1966 static bool is_prev_record_present(pldm_pdr *repo, pldm_pdr_record *record)
1967 {
1968 if (repo->first == record) {
1969 return true;
1970 }
1971
1972 return pldm_pdr_get_prev_record(repo, record) != NULL;
1973 }
1974
1975 /* API to check if FRU RSI of record matches the given record set identifier.
1976 * Returns 1 if the provided FRU record matches the provided record set identifier,
1977 * 0 if it does not, otherwise -EINVAL if the arguments are invalid.
1978 */
1979 LIBPLDM_CC_NONNULL
pldm_pdr_record_matches_fru_rsi(const pldm_pdr_record * record,uint16_t rsi)1980 static int pldm_pdr_record_matches_fru_rsi(const pldm_pdr_record *record,
1981 uint16_t rsi)
1982 {
1983 uint16_t record_fru_rsi = 0;
1984 uint8_t *skip_data = NULL;
1985 uint8_t skip_data_size = 0;
1986 struct pldm_msgbuf _dst;
1987 struct pldm_msgbuf *dst = &_dst;
1988 int rc = 0;
1989
1990 rc = pldm_msgbuf_init_errno(dst, PDR_FRU_RECORD_SET_MIN_SIZE,
1991 record->data, record->size);
1992 if (rc) {
1993 return rc;
1994 }
1995 skip_data_size = sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t);
1996 pldm_msgbuf_span_required(dst, skip_data_size, (void **)&skip_data);
1997 pldm_msgbuf_extract(dst, record_fru_rsi);
1998
1999 rc = pldm_msgbuf_destroy(dst);
2000 if (rc) {
2001 return rc;
2002 }
2003 return record_fru_rsi == rsi;
2004 }
2005
2006 /* API to remove PLDM PDR record from a PLDM PDR repository
2007 */
2008 LIBPLDM_CC_NONNULL_ARGS(1, 2)
pldm_pdr_remove_record(pldm_pdr * repo,pldm_pdr_record * record,pldm_pdr_record * prev)2009 static int pldm_pdr_remove_record(pldm_pdr *repo, pldm_pdr_record *record,
2010 pldm_pdr_record *prev)
2011 {
2012 if (!is_prev_record_present(repo, record)) {
2013 return -EINVAL;
2014 }
2015
2016 assert(repo->size >= record->size);
2017 if (repo->size < record->size) {
2018 return -EOVERFLOW;
2019 }
2020
2021 if (repo->first == record) {
2022 repo->first = record->next;
2023 } else {
2024 if (prev != NULL) {
2025 prev->next = record->next;
2026 }
2027 }
2028
2029 if (repo->last == record) {
2030 repo->last = prev;
2031 if (prev != NULL) {
2032 prev->next = NULL;
2033 }
2034 }
2035 repo->record_count -= 1;
2036 repo->size -= record->size;
2037 free(record->data);
2038 free(record);
2039
2040 return 0;
2041 }
2042
2043 LIBPLDM_ABI_TESTING
pldm_pdr_remove_fru_record_set_by_rsi(pldm_pdr * repo,uint16_t fru_rsi,bool is_remote,uint32_t * record_handle)2044 int pldm_pdr_remove_fru_record_set_by_rsi(pldm_pdr *repo, uint16_t fru_rsi,
2045 bool is_remote,
2046 uint32_t *record_handle)
2047 {
2048 pldm_pdr_record *record;
2049 pldm_pdr_record *prev = NULL;
2050 size_t skip_data_size = sizeof(uint32_t) + sizeof(uint8_t);
2051 uint8_t hdr_type = 0;
2052 int rc = 0;
2053 int match;
2054
2055 if (!repo || !record_handle) {
2056 return -EINVAL;
2057 }
2058 record = repo->first;
2059
2060 while (record != NULL) {
2061 struct pldm_msgbuf _buf;
2062 struct pldm_msgbuf *buf = &_buf;
2063 rc = pldm_msgbuf_init_errno(buf, PDR_FRU_RECORD_SET_MIN_SIZE,
2064 record->data, record->size);
2065 if (rc) {
2066 return rc;
2067 }
2068 pldm_msgbuf_span_required(buf, skip_data_size, NULL);
2069 if ((rc = pldm_msgbuf_extract(buf, hdr_type))) {
2070 return rc;
2071 }
2072 if (record->is_remote != is_remote ||
2073 hdr_type != PLDM_PDR_FRU_RECORD_SET) {
2074 goto cleanup;
2075 }
2076 match = pldm_pdr_record_matches_fru_rsi(record, fru_rsi);
2077 if (match < 0) {
2078 return match;
2079 }
2080 if (match) {
2081 *record_handle = record->record_handle;
2082 prev = pldm_pdr_get_prev_record(repo, record);
2083 return pldm_pdr_remove_record(repo, record, prev);
2084 }
2085 cleanup:
2086 rc = pldm_msgbuf_destroy(buf);
2087 if (rc) {
2088 return rc;
2089 }
2090 record = record->next;
2091 }
2092 return rc;
2093 }
2094