xref: /openbmc/libpldm/src/dsp/pdr.c (revision 890d37a3)
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
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
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
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
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
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 *
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 *
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
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
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
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
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
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
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
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) */
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 
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
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_CC_NONNULL
465 static inline uint16_t next_container_id(pldm_entity_association_tree *tree)
466 {
467 	assert(tree->last_used_container_id != UINT16_MAX);
468 
469 	return ++tree->last_used_container_id;
470 }
471 
472 LIBPLDM_ABI_STABLE
473 pldm_entity pldm_entity_extract(pldm_entity_node *node)
474 {
475 	assert(node != NULL);
476 
477 	return node->entity;
478 }
479 
480 LIBPLDM_ABI_STABLE
481 uint16_t
482 pldm_entity_node_get_remote_container_id(const pldm_entity_node *entity)
483 {
484 	assert(entity != NULL);
485 
486 	return entity->remote_container_id;
487 }
488 
489 LIBPLDM_ABI_STABLE
490 pldm_entity_association_tree *pldm_entity_association_tree_init(void)
491 {
492 	pldm_entity_association_tree *tree =
493 		malloc(sizeof(pldm_entity_association_tree));
494 	if (!tree) {
495 		return NULL;
496 	}
497 	tree->root = NULL;
498 	tree->last_used_container_id = 0;
499 
500 	return tree;
501 }
502 
503 LIBPLDM_CC_NONNULL
504 static pldm_entity_node *find_insertion_at(pldm_entity_node *start,
505 					   uint16_t entity_type)
506 {
507 	/* Insert after the the last node that matches the input entity type, or
508 	 * at the end if no such match occurs
509 	 */
510 	while (start->next_sibling != NULL) {
511 		uint16_t this_type = start->entity.entity_type;
512 		pldm_entity_node *next = start->next_sibling;
513 		if (this_type == entity_type &&
514 		    (this_type != next->entity.entity_type)) {
515 			break;
516 		}
517 		start = start->next_sibling;
518 	}
519 
520 	return start;
521 }
522 
523 LIBPLDM_ABI_STABLE
524 pldm_entity_node *pldm_entity_association_tree_add(
525 	pldm_entity_association_tree *tree, pldm_entity *entity,
526 	uint16_t entity_instance_number, pldm_entity_node *parent,
527 	uint8_t association_type)
528 {
529 	return pldm_entity_association_tree_add_entity(tree, entity,
530 						       entity_instance_number,
531 						       parent, association_type,
532 						       false, true, 0xffff);
533 }
534 
535 LIBPLDM_ABI_STABLE
536 pldm_entity_node *pldm_entity_association_tree_add_entity(
537 	pldm_entity_association_tree *tree, pldm_entity *entity,
538 	uint16_t entity_instance_number, pldm_entity_node *parent,
539 	uint8_t association_type, bool is_remote, bool is_update_container_id,
540 	uint16_t container_id)
541 {
542 	if ((!tree) || (!entity)) {
543 		return NULL;
544 	}
545 
546 	if (entity_instance_number != 0xffff && parent != NULL) {
547 		pldm_entity node;
548 		node.entity_type = entity->entity_type;
549 		node.entity_instance_num = entity_instance_number;
550 		if (pldm_is_current_parent_child(parent, &node)) {
551 			return NULL;
552 		}
553 	}
554 	if (association_type != PLDM_ENTITY_ASSOCIAION_PHYSICAL &&
555 	    association_type != PLDM_ENTITY_ASSOCIAION_LOGICAL) {
556 		return NULL;
557 	}
558 	pldm_entity_node *node = malloc(sizeof(pldm_entity_node));
559 	if (!node) {
560 		return NULL;
561 	}
562 	node->first_child = NULL;
563 	node->next_sibling = NULL;
564 	node->parent.entity_type = 0;
565 	node->parent.entity_instance_num = 0;
566 	node->parent.entity_container_id = 0;
567 	node->entity.entity_type = entity->entity_type;
568 	node->entity.entity_instance_num =
569 		entity_instance_number != 0xffff ? entity_instance_number : 1;
570 	node->association_type = association_type;
571 	node->remote_container_id = 0;
572 	if (tree->root == NULL) {
573 		if (parent != NULL) {
574 			free(node);
575 			return NULL;
576 		}
577 		tree->root = node;
578 		/* container_id 0 here indicates this is the top-most entry */
579 		node->entity.entity_container_id = 0;
580 		node->remote_container_id = node->entity.entity_container_id;
581 	} else if (parent != NULL && parent->first_child == NULL) {
582 		/* Ensure next_container_id() will yield a valid ID */
583 		if (tree->last_used_container_id == UINT16_MAX) {
584 			free(node);
585 			return NULL;
586 		}
587 
588 		parent->first_child = node;
589 		node->parent = parent->entity;
590 
591 		if (is_remote) {
592 			node->remote_container_id = entity->entity_container_id;
593 		}
594 		if (is_update_container_id) {
595 			if (container_id != 0xffff) {
596 				node->entity.entity_container_id = container_id;
597 			} else {
598 				node->entity.entity_container_id =
599 					next_container_id(tree);
600 			}
601 		} else {
602 			node->entity.entity_container_id =
603 				entity->entity_container_id;
604 		}
605 
606 		if (!is_remote) {
607 			node->remote_container_id =
608 				node->entity.entity_container_id;
609 		}
610 	} else {
611 		pldm_entity_node *start = parent == NULL ? tree->root :
612 							   parent->first_child;
613 		pldm_entity_node *prev =
614 			find_insertion_at(start, entity->entity_type);
615 		if (!prev) {
616 			free(node);
617 			return NULL;
618 		}
619 		pldm_entity_node *next = prev->next_sibling;
620 		if (prev->entity.entity_type == entity->entity_type) {
621 			if (prev->entity.entity_instance_num == UINT16_MAX) {
622 				free(node);
623 				return NULL;
624 			}
625 			node->entity.entity_instance_num =
626 				entity_instance_number != 0xffff ?
627 					entity_instance_number :
628 					prev->entity.entity_instance_num + 1;
629 		}
630 		prev->next_sibling = node;
631 		node->parent = prev->parent;
632 		node->next_sibling = next;
633 		node->entity.entity_container_id =
634 			prev->entity.entity_container_id;
635 		node->remote_container_id = entity->entity_container_id;
636 	}
637 	entity->entity_instance_num = node->entity.entity_instance_num;
638 	if (is_update_container_id) {
639 		entity->entity_container_id = node->entity.entity_container_id;
640 	}
641 	return node;
642 }
643 
644 static void get_num_nodes(pldm_entity_node *node, size_t *num)
645 {
646 	if (node == NULL) {
647 		return;
648 	}
649 
650 	++(*num);
651 	get_num_nodes(node->next_sibling, num);
652 	get_num_nodes(node->first_child, num);
653 }
654 
655 static void entity_association_tree_visit(pldm_entity_node *node,
656 					  pldm_entity *entities, size_t *index)
657 {
658 	if (node == NULL) {
659 		return;
660 	}
661 
662 	pldm_entity *entity = &entities[*index];
663 	++(*index);
664 	entity->entity_type = node->entity.entity_type;
665 	entity->entity_instance_num = node->entity.entity_instance_num;
666 	entity->entity_container_id = node->entity.entity_container_id;
667 
668 	entity_association_tree_visit(node->next_sibling, entities, index);
669 	entity_association_tree_visit(node->first_child, entities, index);
670 }
671 
672 LIBPLDM_ABI_STABLE
673 void pldm_entity_association_tree_visit(pldm_entity_association_tree *tree,
674 					pldm_entity **entities, size_t *size)
675 {
676 	if (!tree || !entities || !size) {
677 		return;
678 	}
679 
680 	*size = 0;
681 	if (tree->root == NULL) {
682 		return;
683 	}
684 
685 	get_num_nodes(tree->root, size);
686 	*entities = malloc(*size * sizeof(pldm_entity));
687 	if (!entities) {
688 		return;
689 	}
690 	size_t index = 0;
691 	entity_association_tree_visit(tree->root, *entities, &index);
692 }
693 
694 static void entity_association_tree_destroy(pldm_entity_node *node)
695 {
696 	if (node == NULL) {
697 		return;
698 	}
699 
700 	entity_association_tree_destroy(node->next_sibling);
701 	entity_association_tree_destroy(node->first_child);
702 	free(node);
703 }
704 
705 LIBPLDM_ABI_STABLE
706 void pldm_entity_association_tree_destroy(pldm_entity_association_tree *tree)
707 {
708 	if (!tree) {
709 		return;
710 	}
711 
712 	entity_association_tree_destroy(tree->root);
713 	free(tree);
714 }
715 
716 LIBPLDM_ABI_STABLE
717 bool pldm_entity_is_node_parent(pldm_entity_node *node)
718 {
719 	assert(node != NULL);
720 
721 	return node->first_child != NULL;
722 }
723 
724 LIBPLDM_ABI_STABLE
725 pldm_entity pldm_entity_get_parent(pldm_entity_node *node)
726 {
727 	assert(node != NULL);
728 
729 	return node->parent;
730 }
731 
732 LIBPLDM_ABI_STABLE
733 bool pldm_entity_is_exist_parent(pldm_entity_node *node)
734 {
735 	if (!node) {
736 		return false;
737 	}
738 
739 	if (node->parent.entity_type == 0 &&
740 	    node->parent.entity_instance_num == 0 &&
741 	    node->parent.entity_container_id == 0) {
742 		return false;
743 	}
744 
745 	return true;
746 }
747 
748 LIBPLDM_ABI_STABLE
749 uint8_t pldm_entity_get_num_children(pldm_entity_node *node,
750 				     uint8_t association_type)
751 {
752 	if (!node) {
753 		return 0;
754 	}
755 
756 	if (!(association_type == PLDM_ENTITY_ASSOCIAION_PHYSICAL ||
757 	      association_type == PLDM_ENTITY_ASSOCIAION_LOGICAL)) {
758 		return 0;
759 	}
760 
761 	size_t count = 0;
762 	pldm_entity_node *curr = node->first_child;
763 	while (curr != NULL) {
764 		if (curr->association_type == association_type) {
765 			++count;
766 		}
767 		curr = curr->next_sibling;
768 	}
769 
770 	assert(count < UINT8_MAX);
771 	return count < UINT8_MAX ? count : 0;
772 }
773 
774 LIBPLDM_ABI_STABLE
775 bool pldm_is_current_parent_child(pldm_entity_node *parent, pldm_entity *node)
776 {
777 	if (!parent || !node) {
778 		return false;
779 	}
780 
781 	pldm_entity_node *curr = parent->first_child;
782 	while (curr != NULL) {
783 		if (node->entity_type == curr->entity.entity_type &&
784 		    node->entity_instance_num ==
785 			    curr->entity.entity_instance_num) {
786 			return true;
787 		}
788 		curr = curr->next_sibling;
789 	}
790 
791 	return false;
792 }
793 
794 static int entity_association_pdr_add_children(
795 	pldm_entity_node *curr, pldm_pdr *repo, uint16_t size,
796 	uint8_t contained_count, uint8_t association_type, bool is_remote,
797 	uint16_t terminus_handle, uint32_t record_handle)
798 {
799 	uint8_t *start;
800 	uint8_t *pdr;
801 	int rc;
802 
803 	pdr = calloc(1, size);
804 	if (!pdr) {
805 		return -ENOMEM;
806 	}
807 
808 	start = pdr;
809 
810 	struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)start;
811 	hdr->version = 1;
812 	hdr->record_handle = record_handle;
813 	hdr->type = PLDM_PDR_ENTITY_ASSOCIATION;
814 	hdr->record_change_num = 0;
815 	hdr->length = htole16(size - sizeof(struct pldm_pdr_hdr));
816 	start += sizeof(struct pldm_pdr_hdr);
817 
818 	uint16_t *container_id = (uint16_t *)start;
819 	*container_id = htole16(curr->first_child->entity.entity_container_id);
820 	start += sizeof(uint16_t);
821 	*start = association_type;
822 	start += sizeof(uint8_t);
823 
824 	pldm_entity *entity = (pldm_entity *)start;
825 	entity->entity_type = htole16(curr->entity.entity_type);
826 	entity->entity_instance_num = htole16(curr->entity.entity_instance_num);
827 	entity->entity_container_id = htole16(curr->entity.entity_container_id);
828 	start += sizeof(pldm_entity);
829 
830 	*start = contained_count;
831 	start += sizeof(uint8_t);
832 
833 	pldm_entity_node *node = curr->first_child;
834 	while (node != NULL) {
835 		if (node->association_type == association_type) {
836 			pldm_entity *entity = (pldm_entity *)start;
837 			entity->entity_type = htole16(node->entity.entity_type);
838 			entity->entity_instance_num =
839 				htole16(node->entity.entity_instance_num);
840 			entity->entity_container_id =
841 				htole16(node->entity.entity_container_id);
842 			start += sizeof(pldm_entity);
843 		}
844 		node = node->next_sibling;
845 	}
846 
847 	rc = pldm_pdr_add(repo, pdr, size, is_remote, terminus_handle,
848 			  &record_handle);
849 	free(pdr);
850 	return rc;
851 }
852 
853 static int entity_association_pdr_add_entry(pldm_entity_node *curr,
854 					    pldm_pdr *repo, bool is_remote,
855 					    uint16_t terminus_handle,
856 					    uint32_t record_handle)
857 {
858 	uint8_t num_logical_children = pldm_entity_get_num_children(
859 		curr, PLDM_ENTITY_ASSOCIAION_LOGICAL);
860 	uint8_t num_physical_children = pldm_entity_get_num_children(
861 		curr, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
862 	int rc;
863 
864 	if (num_logical_children) {
865 		uint16_t logical_pdr_size =
866 			sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
867 			sizeof(uint8_t) + sizeof(pldm_entity) +
868 			sizeof(uint8_t) +
869 			(num_logical_children * sizeof(pldm_entity));
870 		rc = entity_association_pdr_add_children(
871 			curr, repo, logical_pdr_size, num_logical_children,
872 			PLDM_ENTITY_ASSOCIAION_LOGICAL, is_remote,
873 			terminus_handle, record_handle);
874 		if (rc < 0) {
875 			return rc;
876 		}
877 	}
878 
879 	if (num_physical_children) {
880 		uint16_t physical_pdr_size =
881 			sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
882 			sizeof(uint8_t) + sizeof(pldm_entity) +
883 			sizeof(uint8_t) +
884 			(num_physical_children * sizeof(pldm_entity));
885 		rc = entity_association_pdr_add_children(
886 			curr, repo, physical_pdr_size, num_physical_children,
887 			PLDM_ENTITY_ASSOCIAION_PHYSICAL, is_remote,
888 			terminus_handle, record_handle);
889 		if (rc < 0) {
890 			return rc;
891 		}
892 	}
893 
894 	return 0;
895 }
896 
897 static bool is_present(pldm_entity entity, pldm_entity **entities,
898 		       size_t num_entities)
899 {
900 	if (entities == NULL || num_entities == 0) {
901 		return true;
902 	}
903 	size_t i = 0;
904 	while (i < num_entities) {
905 		if ((*entities + i)->entity_type == entity.entity_type) {
906 			return true;
907 		}
908 		i++;
909 	}
910 	return false;
911 }
912 
913 static int entity_association_pdr_add(pldm_entity_node *curr, pldm_pdr *repo,
914 				      pldm_entity **entities,
915 				      size_t num_entities, bool is_remote,
916 				      uint16_t terminus_handle,
917 				      uint32_t record_handle)
918 {
919 	int rc;
920 
921 	if (curr == NULL) {
922 		return 0;
923 	}
924 
925 	if (is_present(curr->entity, entities, num_entities)) {
926 		rc = entity_association_pdr_add_entry(
927 			curr, repo, is_remote, terminus_handle, record_handle);
928 		if (rc) {
929 			return rc;
930 		}
931 	}
932 
933 	rc = entity_association_pdr_add(curr->next_sibling, repo, entities,
934 					num_entities, is_remote,
935 					terminus_handle, record_handle);
936 	if (rc) {
937 		return rc;
938 	}
939 
940 	return entity_association_pdr_add(curr->first_child, repo, entities,
941 					  num_entities, is_remote,
942 					  terminus_handle, record_handle);
943 }
944 
945 LIBPLDM_ABI_STABLE
946 int pldm_entity_association_pdr_add(pldm_entity_association_tree *tree,
947 				    pldm_pdr *repo, bool is_remote,
948 				    uint16_t terminus_handle)
949 {
950 	if (!tree || !repo) {
951 		return 0;
952 	}
953 
954 	return entity_association_pdr_add(tree->root, repo, NULL, 0, is_remote,
955 					  terminus_handle, 0);
956 }
957 
958 LIBPLDM_ABI_STABLE
959 int pldm_entity_association_pdr_add_from_node(
960 	pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
961 	size_t num_entities, bool is_remote, uint16_t terminus_handle)
962 {
963 	return pldm_entity_association_pdr_add_from_node_with_record_handle(
964 		node, repo, entities, num_entities, is_remote, terminus_handle,
965 		0);
966 }
967 
968 LIBPLDM_ABI_STABLE
969 int pldm_entity_association_pdr_add_from_node_with_record_handle(
970 	pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
971 	size_t num_entities, bool is_remote, uint16_t terminus_handle,
972 	uint32_t record_handle)
973 {
974 	if (!node || !repo || !entities) {
975 		return -EINVAL;
976 	}
977 
978 	entity_association_pdr_add(node, repo, entities, num_entities,
979 				   is_remote, terminus_handle, record_handle);
980 
981 	return 0;
982 }
983 
984 static void find_entity_ref_in_tree(pldm_entity_node *tree_node,
985 				    pldm_entity entity, pldm_entity_node **node)
986 {
987 	bool is_entity_container_id;
988 	bool is_entity_instance_num;
989 	bool is_type;
990 
991 	if (tree_node == NULL) {
992 		return;
993 	}
994 
995 	is_type = tree_node->entity.entity_type == entity.entity_type;
996 	is_entity_instance_num = tree_node->entity.entity_instance_num ==
997 				 entity.entity_instance_num;
998 	is_entity_container_id = tree_node->entity.entity_container_id ==
999 				 entity.entity_container_id;
1000 
1001 	if (is_type && is_entity_instance_num && is_entity_container_id) {
1002 		*node = tree_node;
1003 		return;
1004 	}
1005 
1006 	find_entity_ref_in_tree(tree_node->first_child, entity, node);
1007 	find_entity_ref_in_tree(tree_node->next_sibling, entity, node);
1008 }
1009 
1010 LIBPLDM_ABI_STABLE
1011 void pldm_find_entity_ref_in_tree(pldm_entity_association_tree *tree,
1012 				  pldm_entity entity, pldm_entity_node **node)
1013 {
1014 	if (!tree || !node) {
1015 		return;
1016 	}
1017 
1018 	find_entity_ref_in_tree(tree->root, entity, node);
1019 }
1020 
1021 LIBPLDM_ABI_STABLE
1022 void pldm_pdr_remove_pdrs_by_terminus_handle(pldm_pdr *repo,
1023 					     uint16_t terminus_handle)
1024 {
1025 	if (!repo) {
1026 		return;
1027 	}
1028 
1029 	bool removed = false;
1030 
1031 	pldm_pdr_record *record = repo->first;
1032 	pldm_pdr_record *prev = NULL;
1033 	while (record != NULL) {
1034 		pldm_pdr_record *next = record->next;
1035 		if (record->terminus_handle == terminus_handle) {
1036 			if (repo->first == record) {
1037 				repo->first = next;
1038 			} else {
1039 				prev->next = next;
1040 			}
1041 			if (repo->last == record) {
1042 				repo->last = prev;
1043 			}
1044 			if (record->data) {
1045 				free(record->data);
1046 			}
1047 			--repo->record_count;
1048 			repo->size -= record->size;
1049 			free(record);
1050 			removed = true;
1051 		} else {
1052 			prev = record;
1053 		}
1054 		record = next;
1055 	}
1056 
1057 	if (removed == true) {
1058 		record = repo->first;
1059 		uint32_t record_handle = 0;
1060 		while (record != NULL) {
1061 			record->record_handle = ++record_handle;
1062 			if (record->data != NULL) {
1063 				struct pldm_pdr_hdr *hdr =
1064 					(struct pldm_pdr_hdr *)(record->data);
1065 				hdr->record_handle =
1066 					htole32(record->record_handle);
1067 			}
1068 			record = record->next;
1069 		}
1070 	}
1071 }
1072 
1073 LIBPLDM_ABI_STABLE
1074 void pldm_pdr_remove_remote_pdrs(pldm_pdr *repo)
1075 {
1076 	if (!repo) {
1077 		return;
1078 	}
1079 
1080 	bool removed = false;
1081 
1082 	pldm_pdr_record *record = repo->first;
1083 	pldm_pdr_record *prev = NULL;
1084 	while (record != NULL) {
1085 		pldm_pdr_record *next = record->next;
1086 		if (record->is_remote == true) {
1087 			if (repo->first == record) {
1088 				repo->first = next;
1089 			} else {
1090 				prev->next = next;
1091 			}
1092 			if (repo->last == record) {
1093 				repo->last = prev;
1094 			}
1095 			if (record->data) {
1096 				free(record->data);
1097 			}
1098 			--repo->record_count;
1099 			repo->size -= record->size;
1100 			free(record);
1101 			removed = true;
1102 		} else {
1103 			prev = record;
1104 		}
1105 		record = next;
1106 	}
1107 
1108 	if (removed == true) {
1109 		record = repo->first;
1110 		uint32_t record_handle = 0;
1111 		while (record != NULL) {
1112 			record->record_handle = ++record_handle;
1113 			if (record->data != NULL) {
1114 				struct pldm_pdr_hdr *hdr =
1115 					(struct pldm_pdr_hdr *)(record->data);
1116 				hdr->record_handle =
1117 					htole32(record->record_handle);
1118 			}
1119 			record = record->next;
1120 		}
1121 	}
1122 }
1123 
1124 LIBPLDM_ABI_STABLE
1125 pldm_pdr_record *pldm_pdr_find_last_in_range(const pldm_pdr *repo,
1126 					     uint32_t first, uint32_t last)
1127 {
1128 	pldm_pdr_record *record = NULL;
1129 	pldm_pdr_record *curr;
1130 
1131 	if (!repo) {
1132 		return NULL;
1133 	}
1134 	for (curr = repo->first; curr; curr = curr->next) {
1135 		if (first > curr->record_handle || last < curr->record_handle) {
1136 			continue;
1137 		}
1138 		if (!record || curr->record_handle > record->record_handle) {
1139 			record = curr;
1140 		}
1141 	}
1142 
1143 	return record;
1144 }
1145 
1146 static void entity_association_tree_find_if_remote(pldm_entity_node *node,
1147 						   pldm_entity *entity,
1148 						   pldm_entity_node **out,
1149 						   bool is_remote)
1150 {
1151 	if (node == NULL) {
1152 		return;
1153 	}
1154 	bool is_entity_type;
1155 	bool is_entity_instance_num;
1156 
1157 	is_entity_type = node->entity.entity_type == entity->entity_type;
1158 	is_entity_instance_num = node->entity.entity_instance_num ==
1159 				 entity->entity_instance_num;
1160 
1161 	if (!is_remote ||
1162 	    node->remote_container_id == entity->entity_container_id) {
1163 		if (is_entity_type && is_entity_instance_num) {
1164 			entity->entity_container_id =
1165 				node->entity.entity_container_id;
1166 			*out = node;
1167 			return;
1168 		}
1169 	}
1170 	entity_association_tree_find_if_remote(node->next_sibling, entity, out,
1171 					       is_remote);
1172 	entity_association_tree_find_if_remote(node->first_child, entity, out,
1173 					       is_remote);
1174 }
1175 
1176 LIBPLDM_ABI_STABLE
1177 pldm_entity_node *pldm_entity_association_tree_find_with_locality(
1178 	pldm_entity_association_tree *tree, pldm_entity *entity, bool is_remote)
1179 {
1180 	if (!tree || !entity) {
1181 		return NULL;
1182 	}
1183 	pldm_entity_node *node = NULL;
1184 	entity_association_tree_find_if_remote(tree->root, entity, &node,
1185 					       is_remote);
1186 	return node;
1187 }
1188 
1189 static void entity_association_tree_find(pldm_entity_node *node,
1190 					 pldm_entity *entity,
1191 					 pldm_entity_node **out)
1192 {
1193 	if (node == NULL) {
1194 		return;
1195 	}
1196 
1197 	if (node->entity.entity_type == entity->entity_type &&
1198 	    node->entity.entity_instance_num == entity->entity_instance_num) {
1199 		entity->entity_container_id = node->entity.entity_container_id;
1200 		*out = node;
1201 		return;
1202 	}
1203 	entity_association_tree_find(node->next_sibling, entity, out);
1204 	entity_association_tree_find(node->first_child, entity, out);
1205 }
1206 
1207 LIBPLDM_ABI_STABLE
1208 pldm_entity_node *
1209 pldm_entity_association_tree_find(pldm_entity_association_tree *tree,
1210 				  pldm_entity *entity)
1211 {
1212 	if (!tree || !entity) {
1213 		return NULL;
1214 	}
1215 
1216 	pldm_entity_node *node = NULL;
1217 	entity_association_tree_find(tree->root, entity, &node);
1218 	return node;
1219 }
1220 
1221 static void entity_association_tree_copy(pldm_entity_node *org_node,
1222 					 pldm_entity_node **new_node)
1223 {
1224 	if (org_node == NULL) {
1225 		return;
1226 	}
1227 	*new_node = malloc(sizeof(pldm_entity_node));
1228 	(*new_node)->parent = org_node->parent;
1229 	(*new_node)->entity = org_node->entity;
1230 	(*new_node)->association_type = org_node->association_type;
1231 	(*new_node)->remote_container_id = org_node->remote_container_id;
1232 	(*new_node)->first_child = NULL;
1233 	(*new_node)->next_sibling = NULL;
1234 	entity_association_tree_copy(org_node->first_child,
1235 				     &((*new_node)->first_child));
1236 	entity_association_tree_copy(org_node->next_sibling,
1237 				     &((*new_node)->next_sibling));
1238 }
1239 
1240 LIBPLDM_ABI_STABLE
1241 void pldm_entity_association_tree_copy_root(
1242 	pldm_entity_association_tree *org_tree,
1243 	pldm_entity_association_tree *new_tree)
1244 {
1245 	assert(org_tree != NULL);
1246 	assert(new_tree != NULL);
1247 
1248 	new_tree->last_used_container_id = org_tree->last_used_container_id;
1249 	entity_association_tree_copy(org_tree->root, &(new_tree->root));
1250 }
1251 
1252 LIBPLDM_ABI_STABLE
1253 void pldm_entity_association_tree_destroy_root(
1254 	pldm_entity_association_tree *tree)
1255 {
1256 	if (!tree) {
1257 		return;
1258 	}
1259 
1260 	entity_association_tree_destroy(tree->root);
1261 	tree->last_used_container_id = 0;
1262 	tree->root = NULL;
1263 }
1264 
1265 LIBPLDM_ABI_STABLE
1266 bool pldm_is_empty_entity_assoc_tree(pldm_entity_association_tree *tree)
1267 {
1268 	return ((tree->root == NULL) ? true : false);
1269 }
1270 
1271 LIBPLDM_ABI_STABLE
1272 void pldm_entity_association_pdr_extract(const uint8_t *pdr, uint16_t pdr_len,
1273 					 size_t *num_entities,
1274 					 pldm_entity **entities)
1275 {
1276 	if (!pdr || !num_entities || !entities) {
1277 		return;
1278 	}
1279 	if (pdr_len < PDR_ENTITY_ASSOCIATION_MIN_SIZE) {
1280 		return;
1281 	}
1282 
1283 	struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)pdr;
1284 	if (hdr->type != PLDM_PDR_ENTITY_ASSOCIATION) {
1285 		return;
1286 	}
1287 
1288 	const uint8_t *start = (uint8_t *)pdr;
1289 	const uint8_t *end LIBPLDM_CC_UNUSED =
1290 		start + sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length);
1291 	start += sizeof(struct pldm_pdr_hdr);
1292 	struct pldm_pdr_entity_association *entity_association_pdr =
1293 		(struct pldm_pdr_entity_association *)start;
1294 	size_t l_num_entities = entity_association_pdr->num_children + 1;
1295 	if (l_num_entities < 2) {
1296 		return;
1297 	}
1298 	if (start + sizeof(struct pldm_pdr_entity_association) +
1299 		    sizeof(pldm_entity) * (l_num_entities - 2) !=
1300 	    end) {
1301 		return;
1302 	}
1303 	pldm_entity *l_entities = malloc(sizeof(pldm_entity) * l_num_entities);
1304 	if (!l_entities) {
1305 		return;
1306 	}
1307 	l_entities[0].entity_type =
1308 		le16toh(entity_association_pdr->container.entity_type);
1309 	l_entities[0].entity_instance_num =
1310 		le16toh(entity_association_pdr->container.entity_instance_num);
1311 	l_entities[0].entity_container_id =
1312 		le16toh(entity_association_pdr->container.entity_container_id);
1313 	pldm_entity *curr_entity = entity_association_pdr->children;
1314 	for (size_t i = 1; i < l_num_entities; i++, curr_entity++) {
1315 		l_entities[i].entity_type = le16toh(curr_entity->entity_type);
1316 		l_entities[i].entity_instance_num =
1317 			le16toh(curr_entity->entity_instance_num);
1318 		l_entities[i].entity_container_id =
1319 			le16toh(curr_entity->entity_container_id);
1320 	}
1321 
1322 	*num_entities = l_num_entities;
1323 	*entities = l_entities;
1324 }
1325 
1326 /* Find the position of record in pldm_pdr repo and place new_record in
1327  * the same position.
1328  */
1329 LIBPLDM_CC_NONNULL
1330 static int pldm_pdr_replace_record(pldm_pdr *repo, pldm_pdr_record *record,
1331 				   pldm_pdr_record *prev,
1332 				   pldm_pdr_record *new_record)
1333 {
1334 	if (repo->size < record->size) {
1335 		return -EOVERFLOW;
1336 	}
1337 
1338 	if (repo->size + new_record->size < new_record->size) {
1339 		return -EOVERFLOW;
1340 	}
1341 
1342 	if (repo->first == record) {
1343 		repo->first = new_record;
1344 	} else {
1345 		prev->next = new_record;
1346 	}
1347 	new_record->next = record->next;
1348 
1349 	if (repo->last == record) {
1350 		repo->last = new_record;
1351 	}
1352 
1353 	repo->size = (repo->size - record->size) + new_record->size;
1354 	return 0;
1355 }
1356 
1357 /* Insert a new record to pldm_pdr repo to a position that comes after
1358  * pldm_pdr_record record.
1359  */
1360 LIBPLDM_CC_NONNULL
1361 static int pldm_pdr_insert_record(pldm_pdr *repo, pldm_pdr_record *record,
1362 				  pldm_pdr_record *new_record)
1363 {
1364 	if (repo->size + new_record->size < new_record->size) {
1365 		return -EOVERFLOW;
1366 	}
1367 
1368 	if (repo->record_count == UINT32_MAX) {
1369 		return -EOVERFLOW;
1370 	}
1371 
1372 	new_record->next = record->next;
1373 	record->next = new_record;
1374 
1375 	if (repo->last == record) {
1376 		repo->last = new_record;
1377 	}
1378 
1379 	repo->size = repo->size + new_record->size;
1380 	++repo->record_count;
1381 	return 0;
1382 }
1383 
1384 /* Find the position of PDR when its record handle is known
1385  */
1386 LIBPLDM_CC_NONNULL
1387 static bool pldm_pdr_find_record_by_handle(pldm_pdr_record **record,
1388 					   pldm_pdr_record **prev,
1389 					   uint32_t record_handle)
1390 {
1391 	while (*record != NULL) {
1392 		if ((*record)->record_handle == record_handle) {
1393 			return true;
1394 		}
1395 		*prev = *record;
1396 		*record = (*record)->next;
1397 	}
1398 	return false;
1399 }
1400 
1401 LIBPLDM_ABI_TESTING
1402 int pldm_entity_association_pdr_add_contained_entity_to_remote_pdr(
1403 	pldm_pdr *repo, pldm_entity *entity, uint32_t pdr_record_handle)
1404 {
1405 	if (!repo || !entity) {
1406 		return -EINVAL;
1407 	}
1408 
1409 	pldm_pdr_record *record = repo->first;
1410 	pldm_pdr_record *prev = repo->first;
1411 	int rc = 0;
1412 	uint16_t header_length = 0;
1413 	uint8_t num_children = 0;
1414 	struct pldm_msgbuf _src;
1415 	struct pldm_msgbuf *src = &_src;
1416 	struct pldm_msgbuf _dst;
1417 	struct pldm_msgbuf *dst = &_dst;
1418 
1419 	pldm_pdr_find_record_by_handle(&record, &prev, pdr_record_handle);
1420 
1421 	if (!record) {
1422 		return -EINVAL;
1423 	}
1424 	// Initialize msg buffer for record and record->data
1425 	rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1426 				    record->data, record->size);
1427 	if (rc) {
1428 		return rc;
1429 	}
1430 
1431 	// check if adding another entity to record causes overflow before
1432 	// allocating memory for new_record.
1433 	if (record->size + sizeof(pldm_entity) < sizeof(pldm_entity)) {
1434 		return -EOVERFLOW;
1435 	}
1436 	pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1437 	if (!new_record) {
1438 		return -ENOMEM;
1439 	}
1440 
1441 	new_record->data = malloc(record->size + sizeof(pldm_entity));
1442 	if (!new_record->data) {
1443 		rc = -ENOMEM;
1444 		goto cleanup_new_record;
1445 	}
1446 
1447 	new_record->record_handle = record->record_handle;
1448 	new_record->size = record->size + sizeof(struct pldm_entity);
1449 	new_record->is_remote = record->is_remote;
1450 
1451 	// Initialize new PDR record with data from original PDR record.
1452 	// Start with adding the header of original PDR
1453 	rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1454 				    new_record->data, new_record->size);
1455 	if (rc) {
1456 		goto cleanup_new_record_data;
1457 	}
1458 
1459 	pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle);
1460 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_version);
1461 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_type);
1462 	pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num);
1463 	// extract the header length from record and increment size with
1464 	// size of pldm_entity before inserting the value into new_record.
1465 	rc = pldm_msgbuf_extract(src, header_length);
1466 	if (rc) {
1467 		goto cleanup_new_record_data;
1468 	}
1469 	static_assert(UINT16_MAX < (SIZE_MAX - sizeof(pldm_entity)),
1470 		      "Fix the following bounds check.");
1471 	if (header_length + sizeof(pldm_entity) > UINT16_MAX) {
1472 		rc = -EOVERFLOW;
1473 		goto cleanup_new_record_data;
1474 	}
1475 	header_length += sizeof(pldm_entity);
1476 	pldm_msgbuf_insert(dst, header_length);
1477 	pldm_msgbuf_copy(dst, src, uint16_t, container_id);
1478 	pldm_msgbuf_copy(dst, src, uint8_t, association_type);
1479 	pldm_msgbuf_copy(dst, src, uint16_t, entity_type);
1480 	pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num);
1481 	pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id);
1482 	// extract value of number of children from record and increment it
1483 	// by 1 before insert the value to new record.
1484 	rc = pldm_msgbuf_extract(src, num_children);
1485 	if (rc) {
1486 		goto cleanup_new_record_data;
1487 	}
1488 	if (num_children == UINT8_MAX) {
1489 		rc = -EOVERFLOW;
1490 		goto cleanup_new_record_data;
1491 	}
1492 	num_children += 1;
1493 	pldm_msgbuf_insert(dst, num_children);
1494 	//Add all children of original PDR to new PDR
1495 	for (int i = 0; i < num_children - 1; i++) {
1496 		pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1497 		pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1498 		pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1499 	}
1500 
1501 	// Add new contained entity as a child of new PDR
1502 	rc = pldm_msgbuf_destroy(src);
1503 	if (rc) {
1504 		goto cleanup_new_record_data;
1505 	}
1506 	rc = pldm_msgbuf_init_errno(src, sizeof(struct pldm_entity), entity,
1507 				    sizeof(struct pldm_entity));
1508 	if (rc) {
1509 		goto cleanup_new_record_data;
1510 	}
1511 	pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1512 	pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1513 	pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1514 
1515 	rc = pldm_msgbuf_destroy(src);
1516 	if (rc) {
1517 		goto cleanup_new_record_data;
1518 	}
1519 	rc = pldm_msgbuf_destroy(dst);
1520 	if (rc) {
1521 		goto cleanup_new_record_data;
1522 	}
1523 
1524 	rc = pldm_pdr_replace_record(repo, record, prev, new_record);
1525 	if (rc) {
1526 		goto cleanup_new_record_data;
1527 	}
1528 
1529 	free(record->data);
1530 	free(record);
1531 	return rc;
1532 cleanup_new_record_data:
1533 	free(new_record->data);
1534 cleanup_new_record:
1535 	free(new_record);
1536 	return rc;
1537 }
1538 
1539 LIBPLDM_ABI_TESTING
1540 int pldm_entity_association_pdr_create_new(pldm_pdr *repo,
1541 					   uint32_t pdr_record_handle,
1542 					   pldm_entity *parent,
1543 					   pldm_entity *entity,
1544 					   uint32_t *entity_record_handle)
1545 {
1546 	if (!repo || !parent || !entity || !entity_record_handle) {
1547 		return -EINVAL;
1548 	}
1549 
1550 	if (pdr_record_handle == UINT32_MAX) {
1551 		return -EOVERFLOW;
1552 	}
1553 
1554 	bool pdr_added = false;
1555 	uint16_t new_pdr_size;
1556 	uint16_t container_id = 0;
1557 	void *container_id_addr;
1558 	struct pldm_msgbuf _dst;
1559 	struct pldm_msgbuf *dst = &_dst;
1560 	struct pldm_msgbuf _src_p;
1561 	struct pldm_msgbuf *src_p = &_src_p;
1562 	struct pldm_msgbuf _src_c;
1563 	struct pldm_msgbuf *src_c = &_src_c;
1564 	int rc = 0;
1565 
1566 	pldm_pdr_record *prev = repo->first;
1567 	pldm_pdr_record *record = repo->first;
1568 	pdr_added = pldm_pdr_find_record_by_handle(&record, &prev,
1569 						   pdr_record_handle);
1570 	if (!pdr_added) {
1571 		return -ENOENT;
1572 	}
1573 
1574 	static_assert(PDR_ENTITY_ASSOCIATION_MIN_SIZE < UINT16_MAX,
1575 		      "Truncation ahead");
1576 	new_pdr_size = PDR_ENTITY_ASSOCIATION_MIN_SIZE;
1577 	pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1578 	if (!new_record) {
1579 		return -ENOMEM;
1580 	}
1581 
1582 	new_record->data = malloc(new_pdr_size);
1583 	if (!new_record->data) {
1584 		rc = -ENOMEM;
1585 		goto cleanup_new_record;
1586 	}
1587 
1588 	// Initialise new PDR to be added with the header, size and handle.
1589 	// Set the position of new PDR
1590 	*entity_record_handle = pdr_record_handle + 1;
1591 	new_record->record_handle = *entity_record_handle;
1592 	new_record->size = new_pdr_size;
1593 	new_record->is_remote = false;
1594 
1595 	rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1596 				    new_record->data, new_record->size);
1597 	if (rc) {
1598 		goto cleanup_new_record_data;
1599 	}
1600 
1601 	// header record handle
1602 	pldm_msgbuf_insert(dst, *entity_record_handle);
1603 	// header version
1604 	pldm_msgbuf_insert_uint8(dst, 1);
1605 	// header type
1606 	pldm_msgbuf_insert_uint8(dst, PLDM_PDR_ENTITY_ASSOCIATION);
1607 	// header change number
1608 	pldm_msgbuf_insert_uint16(dst, 0);
1609 	// header length
1610 	pldm_msgbuf_insert_uint16(dst,
1611 				  (new_pdr_size - sizeof(struct pldm_pdr_hdr)));
1612 
1613 	// Data for new PDR is obtained from parent PDR and new contained entity
1614 	// is added as the child
1615 	rc = pldm_msgbuf_init_errno(src_p, sizeof(struct pldm_entity), parent,
1616 				    sizeof(*parent));
1617 	if (rc) {
1618 		goto cleanup_new_record_data;
1619 	}
1620 
1621 	rc = pldm_msgbuf_init_errno(src_c, sizeof(struct pldm_entity), entity,
1622 				    sizeof(*entity));
1623 	if (rc) {
1624 		goto cleanup_new_record_data;
1625 	}
1626 
1627 	container_id_addr = NULL;
1628 	// extract pointer for container ID and save the address
1629 	rc = pldm_msgbuf_span_required(dst, sizeof(container_id),
1630 				       (void **)&container_id_addr);
1631 	if (rc) {
1632 		goto cleanup_new_record_data;
1633 	}
1634 	assert(container_id_addr);
1635 	pldm_msgbuf_insert_uint8(dst, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
1636 	pldm_msgbuf_copy(dst, src_p, uint16_t, entity_type);
1637 	pldm_msgbuf_copy(dst, src_p, uint16_t, entity_instance_num);
1638 	pldm_msgbuf_copy(dst, src_p, uint16_t, entity_container_id);
1639 	// number of children
1640 	pldm_msgbuf_insert_uint8(dst, 1);
1641 
1642 	// Add new entity as child
1643 	pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_type);
1644 	pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_instance_num);
1645 	// Extract and insert child entity container ID and add same value to
1646 	// container ID of entity
1647 	pldm_msgbuf_extract(src_c, container_id);
1648 	pldm_msgbuf_insert(dst, container_id);
1649 	container_id = htole16(container_id);
1650 	memcpy(container_id_addr, &container_id, sizeof(uint16_t));
1651 
1652 	rc = pldm_msgbuf_destroy(dst);
1653 	if (rc) {
1654 		goto cleanup_new_record_data;
1655 	}
1656 	rc = pldm_msgbuf_destroy(src_p);
1657 	if (rc) {
1658 		goto cleanup_new_record_data;
1659 	}
1660 	rc = pldm_msgbuf_destroy(src_c);
1661 	if (rc) {
1662 		goto cleanup_new_record_data;
1663 	}
1664 
1665 	rc = pldm_pdr_insert_record(repo, record, new_record);
1666 	if (rc) {
1667 		goto cleanup_new_record_data;
1668 	}
1669 
1670 	return rc;
1671 cleanup_new_record_data:
1672 	free(new_record->data);
1673 cleanup_new_record:
1674 	free(new_record);
1675 	return rc;
1676 }
1677 
1678 LIBPLDM_CC_NONNULL
1679 static bool pldm_entity_cmp(const struct pldm_entity *l,
1680 			    const struct pldm_entity *r)
1681 {
1682 	return l->entity_type == r->entity_type &&
1683 	       l->entity_instance_num == r->entity_instance_num &&
1684 	       l->entity_container_id == r->entity_container_id;
1685 }
1686 
1687 /* Find record handle of a PDR record from PDR repo and
1688  * entity
1689  */
1690 LIBPLDM_CC_NONNULL
1691 static int pldm_entity_association_find_record_handle_by_entity(
1692 	pldm_pdr *repo, pldm_entity *entity, bool is_remote,
1693 	uint32_t *record_handle)
1694 {
1695 	uint8_t num_children = 0;
1696 	uint8_t hdr_type = 0;
1697 	int rc = 0;
1698 	size_t skip_data_size = 0;
1699 	pldm_pdr_record *record = repo->first;
1700 	struct pldm_msgbuf _dst;
1701 	struct pldm_msgbuf *dst = &_dst;
1702 
1703 	while (record != NULL) {
1704 		rc = pldm_msgbuf_init_errno(dst,
1705 					    PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1706 					    record->data, record->size);
1707 		if (rc) {
1708 			return rc;
1709 		}
1710 		skip_data_size = sizeof(uint32_t) + sizeof(uint8_t);
1711 		pldm_msgbuf_span_required(dst, skip_data_size, NULL);
1712 		pldm_msgbuf_extract(dst, hdr_type);
1713 		if (record->is_remote != is_remote ||
1714 		    hdr_type != PLDM_PDR_ENTITY_ASSOCIATION) {
1715 			goto cleanup;
1716 		}
1717 		skip_data_size = sizeof(uint16_t) + sizeof(uint16_t) +
1718 				 sizeof(uint16_t) + sizeof(uint8_t) +
1719 				 sizeof(struct pldm_entity);
1720 		pldm_msgbuf_span_required(dst, skip_data_size, NULL);
1721 		pldm_msgbuf_extract(dst, num_children);
1722 		for (int i = 0; i < num_children; ++i) {
1723 			struct pldm_entity e;
1724 
1725 			if ((rc = pldm_msgbuf_extract(dst, e.entity_type)) ||
1726 			    (rc = pldm_msgbuf_extract(dst,
1727 						      e.entity_instance_num)) ||
1728 			    (rc = pldm_msgbuf_extract(dst,
1729 						      e.entity_container_id))) {
1730 				return rc;
1731 			}
1732 
1733 			if (pldm_entity_cmp(entity, &e)) {
1734 				*record_handle = record->record_handle;
1735 				return 0;
1736 			}
1737 		}
1738 	cleanup:
1739 		rc = pldm_msgbuf_destroy(dst);
1740 		if (rc) {
1741 			return rc;
1742 		}
1743 		record = record->next;
1744 	}
1745 	return 0;
1746 }
1747 
1748 LIBPLDM_ABI_TESTING
1749 int pldm_entity_association_pdr_remove_contained_entity(
1750 	pldm_pdr *repo, pldm_entity *entity, bool is_remote,
1751 	uint32_t *pdr_record_handle)
1752 {
1753 	uint16_t header_length = 0;
1754 	uint8_t num_children = 0;
1755 	struct pldm_msgbuf _src;
1756 	struct pldm_msgbuf *src = &_src;
1757 	struct pldm_msgbuf _dst;
1758 	struct pldm_msgbuf *dst = &_dst;
1759 	int rc;
1760 	pldm_pdr_record *record;
1761 	pldm_pdr_record *prev;
1762 
1763 	if (!repo || !entity || !pdr_record_handle) {
1764 		return -EINVAL;
1765 	}
1766 	record = repo->first;
1767 	prev = repo->first;
1768 
1769 	rc = pldm_entity_association_find_record_handle_by_entity(
1770 		repo, entity, is_remote, pdr_record_handle);
1771 	if (rc) {
1772 		return rc;
1773 	}
1774 	pldm_pdr_find_record_by_handle(&record, &prev, *pdr_record_handle);
1775 	if (!record) {
1776 		return -EINVAL;
1777 	}
1778 	// Initialize msg buffer for record and record->data
1779 	rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1780 				    record->data, record->size);
1781 	if (rc) {
1782 		return rc;
1783 	}
1784 	// check if removing an entity from record causes overflow before
1785 	// allocating memory for new_record.
1786 	if (record->size < sizeof(pldm_entity)) {
1787 		return -EOVERFLOW;
1788 	}
1789 	pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1790 	if (!new_record) {
1791 		return -ENOMEM;
1792 	}
1793 	new_record->data = malloc(record->size - sizeof(pldm_entity));
1794 	if (!new_record->data) {
1795 		rc = -ENOMEM;
1796 		goto cleanup_new_record;
1797 	}
1798 	new_record->record_handle = record->record_handle;
1799 	new_record->size = record->size - sizeof(struct pldm_entity);
1800 	new_record->is_remote = record->is_remote;
1801 
1802 	// Initialize new PDR record with data from original PDR record.
1803 	// Start with adding the header of original PDR
1804 	rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1805 				    new_record->data, new_record->size);
1806 	if (rc) {
1807 		goto cleanup_new_record_data;
1808 	}
1809 	pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle);
1810 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_version);
1811 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_type);
1812 	pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num);
1813 	// extract the header length from record and decrement size with
1814 	// size of pldm_entity before inserting the value into new_record.
1815 	rc = pldm_msgbuf_extract(src, header_length);
1816 	if (rc) {
1817 		goto cleanup_new_record_data;
1818 	}
1819 	if (header_length < sizeof(pldm_entity)) {
1820 		rc = -EOVERFLOW;
1821 		goto cleanup_new_record_data;
1822 	}
1823 	header_length -= sizeof(pldm_entity);
1824 	pldm_msgbuf_insert(dst, header_length);
1825 	pldm_msgbuf_copy(dst, src, uint16_t, container_id);
1826 	pldm_msgbuf_copy(dst, src, uint8_t, association_type);
1827 	pldm_msgbuf_copy(dst, src, uint16_t, entity_type);
1828 	pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num);
1829 	pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id);
1830 	// extract value of number of children from record and decrement it
1831 	// by 1 before insert the value to new record.
1832 	rc = pldm_msgbuf_extract(src, num_children);
1833 	if (rc) {
1834 		goto cleanup_new_record_data;
1835 	}
1836 	if (num_children == 1) {
1837 		prev->next = record->next;
1838 		free(record->data);
1839 		free(record);
1840 		goto cleanup_new_record_data;
1841 	} else if (num_children < 1) {
1842 		rc = -EOVERFLOW;
1843 		goto cleanup_new_record_data;
1844 	}
1845 	num_children -= 1;
1846 	pldm_msgbuf_insert(dst, num_children);
1847 	//Add all children of original PDR to new PDR
1848 	for (int i = 0; i < num_children + 1; ++i) {
1849 		struct pldm_entity e;
1850 
1851 		if ((rc = pldm_msgbuf_extract(src, e.entity_type)) ||
1852 		    (rc = pldm_msgbuf_extract(src, e.entity_instance_num)) ||
1853 		    (rc = pldm_msgbuf_extract(src, e.entity_container_id))) {
1854 			goto cleanup_new_record_data;
1855 		}
1856 
1857 		if (pldm_entity_cmp(entity, &e)) {
1858 			continue;
1859 		}
1860 
1861 		pldm_msgbuf_insert(dst, e.entity_type);
1862 		pldm_msgbuf_insert(dst, e.entity_instance_num);
1863 		pldm_msgbuf_insert(dst, e.entity_container_id);
1864 	}
1865 
1866 	if ((rc = pldm_msgbuf_destroy(src)) ||
1867 	    (rc = pldm_msgbuf_destroy(dst)) ||
1868 	    (rc = pldm_pdr_replace_record(repo, record, prev, new_record))) {
1869 		goto cleanup_new_record_data;
1870 	}
1871 
1872 	free(record->data);
1873 	free(record);
1874 	return rc;
1875 
1876 cleanup_new_record_data:
1877 	free(new_record->data);
1878 cleanup_new_record:
1879 	free(new_record);
1880 	return rc;
1881 }
1882 
1883 /* API to find the PDR record that is previous to a given PLDM PDR
1884  * record in a given PLDM PDR repository
1885  */
1886 LIBPLDM_CC_NONNULL
1887 static pldm_pdr_record *pldm_pdr_get_prev_record(pldm_pdr *repo,
1888 						 pldm_pdr_record *record)
1889 {
1890 	pldm_pdr_record *prev = NULL;
1891 	pldm_pdr_record *curr = repo->first;
1892 
1893 	while (curr != NULL) {
1894 		if (curr->record_handle == record->record_handle) {
1895 			break;
1896 		}
1897 		prev = curr;
1898 		curr = curr->next;
1899 	}
1900 	return prev;
1901 }
1902 
1903 /* API to check if a PLDM PDR record is present in a PLDM PDR repository
1904  */
1905 LIBPLDM_CC_NONNULL
1906 static bool is_prev_record_present(pldm_pdr *repo, pldm_pdr_record *record)
1907 {
1908 	if (repo->first == record) {
1909 		return true;
1910 	}
1911 
1912 	return pldm_pdr_get_prev_record(repo, record) != NULL;
1913 }
1914 
1915 /* API to check if FRU RSI of record matches the given record set identifier.
1916  * Returns 1 if the provided FRU record matches the provided record set identifier,
1917  * 0 if it does not, otherwise -EINVAL if the arguments are invalid.
1918  */
1919 LIBPLDM_CC_NONNULL
1920 static int pldm_pdr_record_matches_fru_rsi(const pldm_pdr_record *record,
1921 					   uint16_t rsi)
1922 {
1923 	uint16_t record_fru_rsi = 0;
1924 	uint8_t *skip_data = NULL;
1925 	uint8_t skip_data_size = 0;
1926 	struct pldm_msgbuf _dst;
1927 	struct pldm_msgbuf *dst = &_dst;
1928 	int rc = 0;
1929 
1930 	rc = pldm_msgbuf_init_errno(dst, PDR_FRU_RECORD_SET_MIN_SIZE,
1931 				    record->data, record->size);
1932 	if (rc) {
1933 		return rc;
1934 	}
1935 	skip_data_size = sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t);
1936 	pldm_msgbuf_span_required(dst, skip_data_size, (void **)&skip_data);
1937 	pldm_msgbuf_extract(dst, record_fru_rsi);
1938 
1939 	rc = pldm_msgbuf_destroy(dst);
1940 	if (rc) {
1941 		return rc;
1942 	}
1943 	return record_fru_rsi == rsi;
1944 }
1945 
1946 /* API to remove PLDM PDR record from a PLDM PDR repository
1947  */
1948 LIBPLDM_CC_NONNULL_ARGS(1, 2)
1949 static int pldm_pdr_remove_record(pldm_pdr *repo, pldm_pdr_record *record,
1950 				  pldm_pdr_record *prev)
1951 {
1952 	if (!is_prev_record_present(repo, record)) {
1953 		return -EINVAL;
1954 	}
1955 
1956 	assert(repo->size >= record->size);
1957 	if (repo->size < record->size) {
1958 		return -EOVERFLOW;
1959 	}
1960 
1961 	if (repo->first == record) {
1962 		repo->first = record->next;
1963 	} else {
1964 		if (prev != NULL) {
1965 			prev->next = record->next;
1966 		}
1967 	}
1968 
1969 	if (repo->last == record) {
1970 		repo->last = prev;
1971 		if (prev != NULL) {
1972 			prev->next = NULL;
1973 		}
1974 	}
1975 	repo->record_count -= 1;
1976 	repo->size -= record->size;
1977 	free(record->data);
1978 	free(record);
1979 
1980 	return 0;
1981 }
1982 
1983 LIBPLDM_ABI_TESTING
1984 int pldm_pdr_remove_fru_record_set_by_rsi(pldm_pdr *repo, uint16_t fru_rsi,
1985 					  bool is_remote,
1986 					  uint32_t *record_handle)
1987 {
1988 	pldm_pdr_record *record;
1989 	pldm_pdr_record *prev = NULL;
1990 	size_t skip_data_size = sizeof(uint32_t) + sizeof(uint8_t);
1991 	uint8_t hdr_type = 0;
1992 	int rc = 0;
1993 	int match;
1994 
1995 	if (!repo || !record_handle) {
1996 		return -EINVAL;
1997 	}
1998 	record = repo->first;
1999 
2000 	while (record != NULL) {
2001 		struct pldm_msgbuf _buf;
2002 		struct pldm_msgbuf *buf = &_buf;
2003 		rc = pldm_msgbuf_init_errno(buf, PDR_FRU_RECORD_SET_MIN_SIZE,
2004 					    record->data, record->size);
2005 		if (rc) {
2006 			return rc;
2007 		}
2008 		pldm_msgbuf_span_required(buf, skip_data_size, NULL);
2009 		if ((rc = pldm_msgbuf_extract(buf, hdr_type))) {
2010 			return rc;
2011 		}
2012 		if (record->is_remote != is_remote ||
2013 		    hdr_type != PLDM_PDR_FRU_RECORD_SET) {
2014 			goto cleanup;
2015 		}
2016 		match = pldm_pdr_record_matches_fru_rsi(record, fru_rsi);
2017 		if (match < 0) {
2018 			return match;
2019 		}
2020 		if (match) {
2021 			*record_handle = record->record_handle;
2022 			prev = pldm_pdr_get_prev_record(repo, record);
2023 			return pldm_pdr_remove_record(repo, record, prev);
2024 		}
2025 	cleanup:
2026 		rc = pldm_msgbuf_destroy(buf);
2027 		if (rc) {
2028 			return rc;
2029 		}
2030 		record = record->next;
2031 	}
2032 	return rc;
2033 }
2034