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