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