xref: /openbmc/libpldm/src/dsp/pdr.c (revision 60582150f3eb15c7636526869c86b44b89755df7)
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_ABI_STABLE
465 pldm_entity pldm_entity_extract(pldm_entity_node *node)
466 {
467 	assert(node != NULL);
468 
469 	return node->entity;
470 }
471 
472 LIBPLDM_ABI_STABLE
473 uint16_t
474 pldm_entity_node_get_remote_container_id(const pldm_entity_node *entity)
475 {
476 	assert(entity != NULL);
477 
478 	return entity->remote_container_id;
479 }
480 
481 LIBPLDM_ABI_STABLE
482 pldm_entity_association_tree *pldm_entity_association_tree_init(void)
483 {
484 	pldm_entity_association_tree *tree =
485 		malloc(sizeof(pldm_entity_association_tree));
486 	if (!tree) {
487 		return NULL;
488 	}
489 	tree->root = NULL;
490 	tree->last_used_container_id = 0;
491 
492 	return tree;
493 }
494 
495 LIBPLDM_CC_NONNULL
496 static pldm_entity_node *find_insertion_at(pldm_entity_node *start,
497 					   uint16_t entity_type)
498 {
499 	/* Insert after the the last node that matches the input entity type, or
500 	 * at the end if no such match occurs
501 	 */
502 	while (start->next_sibling != NULL) {
503 		uint16_t this_type = start->entity.entity_type;
504 		pldm_entity_node *next = start->next_sibling;
505 		if (this_type == entity_type &&
506 		    (this_type != next->entity.entity_type)) {
507 			break;
508 		}
509 		start = start->next_sibling;
510 	}
511 
512 	return start;
513 }
514 
515 LIBPLDM_ABI_STABLE
516 pldm_entity_node *pldm_entity_association_tree_add(
517 	pldm_entity_association_tree *tree, pldm_entity *entity,
518 	uint16_t entity_instance_number, pldm_entity_node *parent,
519 	uint8_t association_type)
520 {
521 	return pldm_entity_association_tree_add_entity(tree, entity,
522 						       entity_instance_number,
523 						       parent, association_type,
524 						       false, true, 0xffff);
525 }
526 
527 LIBPLDM_ABI_STABLE
528 pldm_entity_node *pldm_entity_association_tree_add_entity(
529 	pldm_entity_association_tree *tree, pldm_entity *entity,
530 	uint16_t entity_instance_number, pldm_entity_node *parent,
531 	uint8_t association_type, bool is_remote, bool is_update_container_id,
532 	uint16_t container_id)
533 {
534 	if ((!tree) || (!entity)) {
535 		return NULL;
536 	}
537 
538 	if (entity_instance_number != 0xffff && parent != NULL) {
539 		pldm_entity node;
540 		node.entity_type = entity->entity_type;
541 		node.entity_instance_num = entity_instance_number;
542 		if (pldm_is_current_parent_child(parent, &node)) {
543 			return NULL;
544 		}
545 	}
546 	if (association_type != PLDM_ENTITY_ASSOCIAION_PHYSICAL &&
547 	    association_type != PLDM_ENTITY_ASSOCIAION_LOGICAL) {
548 		return NULL;
549 	}
550 	pldm_entity_node *node = malloc(sizeof(pldm_entity_node));
551 	if (!node) {
552 		return NULL;
553 	}
554 	node->first_child = NULL;
555 	node->next_sibling = NULL;
556 	node->parent.entity_type = 0;
557 	node->parent.entity_instance_num = 0;
558 	node->parent.entity_container_id = 0;
559 	node->entity.entity_type = entity->entity_type;
560 	node->entity.entity_instance_num =
561 		entity_instance_number != 0xffff ? entity_instance_number : 1;
562 	node->association_type = association_type;
563 	node->remote_container_id = 0;
564 	if (tree->root == NULL) {
565 		if (parent != NULL) {
566 			free(node);
567 			return NULL;
568 		}
569 		tree->root = node;
570 		/* container_id 0 here indicates this is the top-most entry */
571 		node->entity.entity_container_id = 0;
572 		node->remote_container_id = node->entity.entity_container_id;
573 	} else if (parent != NULL && parent->first_child == NULL) {
574 		/* Ensure next_container_id() will yield a valid ID */
575 		if (tree->last_used_container_id == UINT16_MAX) {
576 			free(node);
577 			return NULL;
578 		}
579 
580 		parent->first_child = node;
581 		node->parent = parent->entity;
582 
583 		if (is_remote) {
584 			node->remote_container_id = entity->entity_container_id;
585 		}
586 		if (is_update_container_id) {
587 			if (container_id != 0xffff) {
588 				node->entity.entity_container_id = container_id;
589 			} else {
590 				/* We will have returned above */
591 				assert(tree->last_used_container_id !=
592 				       UINT16_MAX);
593 				node->entity.entity_container_id =
594 					++tree->last_used_container_id;
595 			}
596 		} else {
597 			node->entity.entity_container_id =
598 				entity->entity_container_id;
599 		}
600 
601 		if (!is_remote) {
602 			node->remote_container_id =
603 				node->entity.entity_container_id;
604 		}
605 	} else {
606 		pldm_entity_node *start = parent == NULL ? tree->root :
607 							   parent->first_child;
608 		pldm_entity_node *prev =
609 			find_insertion_at(start, entity->entity_type);
610 		if (!prev) {
611 			free(node);
612 			return NULL;
613 		}
614 		pldm_entity_node *next = prev->next_sibling;
615 		if (prev->entity.entity_type == entity->entity_type) {
616 			if (prev->entity.entity_instance_num == UINT16_MAX) {
617 				free(node);
618 				return NULL;
619 			}
620 			node->entity.entity_instance_num =
621 				entity_instance_number != 0xffff ?
622 					entity_instance_number :
623 					prev->entity.entity_instance_num + 1;
624 		}
625 		prev->next_sibling = node;
626 		node->parent = prev->parent;
627 		node->next_sibling = next;
628 		node->entity.entity_container_id =
629 			prev->entity.entity_container_id;
630 		node->remote_container_id = entity->entity_container_id;
631 	}
632 	entity->entity_instance_num = node->entity.entity_instance_num;
633 	if (is_update_container_id) {
634 		entity->entity_container_id = node->entity.entity_container_id;
635 	}
636 	return node;
637 }
638 
639 static void get_num_nodes(pldm_entity_node *node, size_t *num)
640 {
641 	if (node == NULL) {
642 		return;
643 	}
644 
645 	++(*num);
646 	get_num_nodes(node->next_sibling, num);
647 	get_num_nodes(node->first_child, num);
648 }
649 
650 static void entity_association_tree_visit(pldm_entity_node *node,
651 					  pldm_entity *entities, size_t *index)
652 {
653 	if (node == NULL) {
654 		return;
655 	}
656 
657 	pldm_entity *entity = &entities[*index];
658 	++(*index);
659 	entity->entity_type = node->entity.entity_type;
660 	entity->entity_instance_num = node->entity.entity_instance_num;
661 	entity->entity_container_id = node->entity.entity_container_id;
662 
663 	entity_association_tree_visit(node->next_sibling, entities, index);
664 	entity_association_tree_visit(node->first_child, entities, index);
665 }
666 
667 LIBPLDM_ABI_STABLE
668 void pldm_entity_association_tree_visit(pldm_entity_association_tree *tree,
669 					pldm_entity **entities, size_t *size)
670 {
671 	if (!tree || !entities || !size) {
672 		return;
673 	}
674 
675 	*size = 0;
676 	if (tree->root == NULL) {
677 		return;
678 	}
679 
680 	get_num_nodes(tree->root, size);
681 	*entities = malloc(*size * sizeof(pldm_entity));
682 	if (!entities) {
683 		return;
684 	}
685 	size_t index = 0;
686 	entity_association_tree_visit(tree->root, *entities, &index);
687 }
688 
689 static void entity_association_tree_destroy(pldm_entity_node *node)
690 {
691 	if (node == NULL) {
692 		return;
693 	}
694 
695 	entity_association_tree_destroy(node->next_sibling);
696 	entity_association_tree_destroy(node->first_child);
697 	free(node);
698 }
699 
700 LIBPLDM_ABI_STABLE
701 void pldm_entity_association_tree_destroy(pldm_entity_association_tree *tree)
702 {
703 	if (!tree) {
704 		return;
705 	}
706 
707 	entity_association_tree_destroy(tree->root);
708 	free(tree);
709 }
710 
711 LIBPLDM_ABI_STABLE
712 bool pldm_entity_is_node_parent(pldm_entity_node *node)
713 {
714 	assert(node != NULL);
715 
716 	return node->first_child != NULL;
717 }
718 
719 LIBPLDM_ABI_STABLE
720 pldm_entity pldm_entity_get_parent(pldm_entity_node *node)
721 {
722 	assert(node != NULL);
723 
724 	return node->parent;
725 }
726 
727 LIBPLDM_ABI_STABLE
728 bool pldm_entity_is_exist_parent(pldm_entity_node *node)
729 {
730 	if (!node) {
731 		return false;
732 	}
733 
734 	if (node->parent.entity_type == 0 &&
735 	    node->parent.entity_instance_num == 0 &&
736 	    node->parent.entity_container_id == 0) {
737 		return false;
738 	}
739 
740 	return true;
741 }
742 
743 LIBPLDM_ABI_STABLE
744 uint8_t pldm_entity_get_num_children(pldm_entity_node *node,
745 				     uint8_t association_type)
746 {
747 	if (!node) {
748 		return 0;
749 	}
750 
751 	if (!(association_type == PLDM_ENTITY_ASSOCIAION_PHYSICAL ||
752 	      association_type == PLDM_ENTITY_ASSOCIAION_LOGICAL)) {
753 		return 0;
754 	}
755 
756 	size_t count = 0;
757 	pldm_entity_node *curr = node->first_child;
758 	while (curr != NULL) {
759 		if (curr->association_type == association_type) {
760 			++count;
761 		}
762 		curr = curr->next_sibling;
763 	}
764 
765 	assert(count < UINT8_MAX);
766 	return count < UINT8_MAX ? count : 0;
767 }
768 
769 LIBPLDM_ABI_STABLE
770 bool pldm_is_current_parent_child(pldm_entity_node *parent, pldm_entity *node)
771 {
772 	if (!parent || !node) {
773 		return false;
774 	}
775 
776 	pldm_entity_node *curr = parent->first_child;
777 	while (curr != NULL) {
778 		if (node->entity_type == curr->entity.entity_type &&
779 		    node->entity_instance_num ==
780 			    curr->entity.entity_instance_num) {
781 			return true;
782 		}
783 		curr = curr->next_sibling;
784 	}
785 
786 	return false;
787 }
788 
789 static int entity_association_pdr_add_children(
790 	pldm_entity_node *curr, pldm_pdr *repo, uint16_t size,
791 	uint8_t contained_count, uint8_t association_type, bool is_remote,
792 	uint16_t terminus_handle, uint32_t record_handle)
793 {
794 	uint8_t *start;
795 	uint8_t *pdr;
796 	int rc;
797 
798 	pdr = calloc(1, size);
799 	if (!pdr) {
800 		return -ENOMEM;
801 	}
802 
803 	start = pdr;
804 
805 	struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)start;
806 	hdr->version = 1;
807 	hdr->record_handle = record_handle;
808 	hdr->type = PLDM_PDR_ENTITY_ASSOCIATION;
809 	hdr->record_change_num = 0;
810 	hdr->length = htole16(size - sizeof(struct pldm_pdr_hdr));
811 	start += sizeof(struct pldm_pdr_hdr);
812 
813 	uint16_t *container_id = (uint16_t *)start;
814 	*container_id = htole16(curr->first_child->entity.entity_container_id);
815 	start += sizeof(uint16_t);
816 	*start = association_type;
817 	start += sizeof(uint8_t);
818 
819 	pldm_entity *entity = (pldm_entity *)start;
820 	entity->entity_type = htole16(curr->entity.entity_type);
821 	entity->entity_instance_num = htole16(curr->entity.entity_instance_num);
822 	entity->entity_container_id = htole16(curr->entity.entity_container_id);
823 	start += sizeof(pldm_entity);
824 
825 	*start = contained_count;
826 	start += sizeof(uint8_t);
827 
828 	pldm_entity_node *node = curr->first_child;
829 	while (node != NULL) {
830 		if (node->association_type == association_type) {
831 			pldm_entity *entity = (pldm_entity *)start;
832 			entity->entity_type = htole16(node->entity.entity_type);
833 			entity->entity_instance_num =
834 				htole16(node->entity.entity_instance_num);
835 			entity->entity_container_id =
836 				htole16(node->entity.entity_container_id);
837 			start += sizeof(pldm_entity);
838 		}
839 		node = node->next_sibling;
840 	}
841 
842 	rc = pldm_pdr_add(repo, pdr, size, is_remote, terminus_handle,
843 			  &record_handle);
844 	free(pdr);
845 	return rc;
846 }
847 
848 static int entity_association_pdr_add_entry(pldm_entity_node *curr,
849 					    pldm_pdr *repo, bool is_remote,
850 					    uint16_t terminus_handle,
851 					    uint32_t record_handle)
852 {
853 	uint8_t num_logical_children = pldm_entity_get_num_children(
854 		curr, PLDM_ENTITY_ASSOCIAION_LOGICAL);
855 	uint8_t num_physical_children = pldm_entity_get_num_children(
856 		curr, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
857 	int rc;
858 
859 	if (num_logical_children) {
860 		uint16_t logical_pdr_size =
861 			sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
862 			sizeof(uint8_t) + sizeof(pldm_entity) +
863 			sizeof(uint8_t) +
864 			(num_logical_children * sizeof(pldm_entity));
865 		rc = entity_association_pdr_add_children(
866 			curr, repo, logical_pdr_size, num_logical_children,
867 			PLDM_ENTITY_ASSOCIAION_LOGICAL, is_remote,
868 			terminus_handle, record_handle);
869 		if (rc < 0) {
870 			return rc;
871 		}
872 	}
873 
874 	if (num_physical_children) {
875 		uint16_t physical_pdr_size =
876 			sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
877 			sizeof(uint8_t) + sizeof(pldm_entity) +
878 			sizeof(uint8_t) +
879 			(num_physical_children * sizeof(pldm_entity));
880 		rc = entity_association_pdr_add_children(
881 			curr, repo, physical_pdr_size, num_physical_children,
882 			PLDM_ENTITY_ASSOCIAION_PHYSICAL, is_remote,
883 			terminus_handle, record_handle);
884 		if (rc < 0) {
885 			return rc;
886 		}
887 	}
888 
889 	return 0;
890 }
891 
892 static bool is_present(pldm_entity entity, pldm_entity **entities,
893 		       size_t num_entities)
894 {
895 	if (entities == NULL || num_entities == 0) {
896 		return true;
897 	}
898 	size_t i = 0;
899 	while (i < num_entities) {
900 		if ((*entities + i)->entity_type == entity.entity_type) {
901 			return true;
902 		}
903 		i++;
904 	}
905 	return false;
906 }
907 
908 static int entity_association_pdr_add(pldm_entity_node *curr, pldm_pdr *repo,
909 				      pldm_entity **entities,
910 				      size_t num_entities, bool is_remote,
911 				      uint16_t terminus_handle,
912 				      uint32_t record_handle)
913 {
914 	int rc;
915 
916 	if (curr == NULL) {
917 		return 0;
918 	}
919 
920 	if (is_present(curr->entity, entities, num_entities)) {
921 		rc = entity_association_pdr_add_entry(
922 			curr, repo, is_remote, terminus_handle, record_handle);
923 		if (rc) {
924 			return rc;
925 		}
926 	}
927 
928 	rc = entity_association_pdr_add(curr->next_sibling, repo, entities,
929 					num_entities, is_remote,
930 					terminus_handle, record_handle);
931 	if (rc) {
932 		return rc;
933 	}
934 
935 	return entity_association_pdr_add(curr->first_child, repo, entities,
936 					  num_entities, is_remote,
937 					  terminus_handle, record_handle);
938 }
939 
940 LIBPLDM_ABI_STABLE
941 int pldm_entity_association_pdr_add(pldm_entity_association_tree *tree,
942 				    pldm_pdr *repo, bool is_remote,
943 				    uint16_t terminus_handle)
944 {
945 	if (!tree || !repo) {
946 		return 0;
947 	}
948 
949 	return entity_association_pdr_add(tree->root, repo, NULL, 0, is_remote,
950 					  terminus_handle, 0);
951 }
952 
953 LIBPLDM_ABI_STABLE
954 int pldm_entity_association_pdr_add_from_node(
955 	pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
956 	size_t num_entities, bool is_remote, uint16_t terminus_handle)
957 {
958 	return pldm_entity_association_pdr_add_from_node_with_record_handle(
959 		node, repo, entities, num_entities, is_remote, terminus_handle,
960 		0);
961 }
962 
963 LIBPLDM_ABI_STABLE
964 int pldm_entity_association_pdr_add_from_node_with_record_handle(
965 	pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
966 	size_t num_entities, bool is_remote, uint16_t terminus_handle,
967 	uint32_t record_handle)
968 {
969 	if (!node || !repo || !entities) {
970 		return -EINVAL;
971 	}
972 
973 	entity_association_pdr_add(node, repo, entities, num_entities,
974 				   is_remote, terminus_handle, record_handle);
975 
976 	return 0;
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
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 	const uint8_t *end LIBPLDM_CC_UNUSED =
1321 		start + sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length);
1322 	start += sizeof(struct pldm_pdr_hdr);
1323 	struct pldm_pdr_entity_association *entity_association_pdr =
1324 		(struct pldm_pdr_entity_association *)start;
1325 	size_t l_num_entities = entity_association_pdr->num_children + 1;
1326 	if (l_num_entities < 2) {
1327 		return;
1328 	}
1329 	if (start + sizeof(struct pldm_pdr_entity_association) +
1330 		    sizeof(pldm_entity) * (l_num_entities - 2) !=
1331 	    end) {
1332 		return;
1333 	}
1334 	pldm_entity *l_entities = malloc(sizeof(pldm_entity) * l_num_entities);
1335 	if (!l_entities) {
1336 		return;
1337 	}
1338 	l_entities[0].entity_type =
1339 		le16toh(entity_association_pdr->container.entity_type);
1340 	l_entities[0].entity_instance_num =
1341 		le16toh(entity_association_pdr->container.entity_instance_num);
1342 	l_entities[0].entity_container_id =
1343 		le16toh(entity_association_pdr->container.entity_container_id);
1344 	pldm_entity *curr_entity = entity_association_pdr->children;
1345 	for (size_t i = 1; i < l_num_entities; i++, curr_entity++) {
1346 		l_entities[i].entity_type = le16toh(curr_entity->entity_type);
1347 		l_entities[i].entity_instance_num =
1348 			le16toh(curr_entity->entity_instance_num);
1349 		l_entities[i].entity_container_id =
1350 			le16toh(curr_entity->entity_container_id);
1351 	}
1352 
1353 	*num_entities = l_num_entities;
1354 	*entities = l_entities;
1355 }
1356 
1357 /* Find the position of record in pldm_pdr repo and place new_record in
1358  * the same position.
1359  */
1360 LIBPLDM_CC_NONNULL
1361 static int pldm_pdr_replace_record(pldm_pdr *repo, pldm_pdr_record *record,
1362 				   pldm_pdr_record *prev,
1363 				   pldm_pdr_record *new_record)
1364 {
1365 	if (repo->size < record->size) {
1366 		return -EOVERFLOW;
1367 	}
1368 
1369 	if (repo->size + new_record->size < new_record->size) {
1370 		return -EOVERFLOW;
1371 	}
1372 
1373 	if (repo->first == record) {
1374 		repo->first = new_record;
1375 	} else {
1376 		prev->next = new_record;
1377 	}
1378 	new_record->next = record->next;
1379 
1380 	if (repo->last == record) {
1381 		repo->last = new_record;
1382 	}
1383 
1384 	repo->size = (repo->size - record->size) + new_record->size;
1385 	return 0;
1386 }
1387 
1388 /* Insert a new record to pldm_pdr repo to a position that comes after
1389  * pldm_pdr_record record.
1390  */
1391 LIBPLDM_CC_NONNULL
1392 static int pldm_pdr_insert_record(pldm_pdr *repo, pldm_pdr_record *record,
1393 				  pldm_pdr_record *new_record)
1394 {
1395 	if (repo->size + new_record->size < new_record->size) {
1396 		return -EOVERFLOW;
1397 	}
1398 
1399 	if (repo->record_count == UINT32_MAX) {
1400 		return -EOVERFLOW;
1401 	}
1402 
1403 	new_record->next = record->next;
1404 	record->next = new_record;
1405 
1406 	if (repo->last == record) {
1407 		repo->last = new_record;
1408 	}
1409 
1410 	repo->size = repo->size + new_record->size;
1411 	++repo->record_count;
1412 	return 0;
1413 }
1414 
1415 /* Find the position of PDR when its record handle is known
1416  */
1417 LIBPLDM_CC_NONNULL
1418 static bool pldm_pdr_find_record_by_handle(pldm_pdr_record **record,
1419 					   pldm_pdr_record **prev,
1420 					   uint32_t record_handle)
1421 {
1422 	while (*record != NULL) {
1423 		if ((*record)->record_handle == record_handle) {
1424 			return true;
1425 		}
1426 		*prev = *record;
1427 		*record = (*record)->next;
1428 	}
1429 	return false;
1430 }
1431 
1432 LIBPLDM_ABI_TESTING
1433 int pldm_entity_association_pdr_add_contained_entity_to_remote_pdr(
1434 	pldm_pdr *repo, pldm_entity *entity, uint32_t pdr_record_handle)
1435 {
1436 	if (!repo || !entity) {
1437 		return -EINVAL;
1438 	}
1439 
1440 	pldm_pdr_record *record = repo->first;
1441 	pldm_pdr_record *prev = repo->first;
1442 	int rc = 0;
1443 	uint16_t header_length = 0;
1444 	uint8_t num_children = 0;
1445 	struct pldm_msgbuf _src;
1446 	struct pldm_msgbuf *src = &_src;
1447 	struct pldm_msgbuf _dst;
1448 	struct pldm_msgbuf *dst = &_dst;
1449 
1450 	pldm_pdr_find_record_by_handle(&record, &prev, pdr_record_handle);
1451 
1452 	if (!record) {
1453 		return -EINVAL;
1454 	}
1455 	// Initialize msg buffer for record and record->data
1456 	rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1457 				    record->data, record->size);
1458 	if (rc) {
1459 		return rc;
1460 	}
1461 
1462 	// check if adding another entity to record causes overflow before
1463 	// allocating memory for new_record.
1464 	if (record->size + sizeof(pldm_entity) < sizeof(pldm_entity)) {
1465 		return -EOVERFLOW;
1466 	}
1467 	pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1468 	if (!new_record) {
1469 		return -ENOMEM;
1470 	}
1471 
1472 	new_record->data = malloc(record->size + sizeof(pldm_entity));
1473 	if (!new_record->data) {
1474 		rc = -ENOMEM;
1475 		goto cleanup_new_record;
1476 	}
1477 
1478 	new_record->record_handle = record->record_handle;
1479 	new_record->size = record->size + sizeof(struct pldm_entity);
1480 	new_record->is_remote = record->is_remote;
1481 
1482 	// Initialize new PDR record with data from original PDR record.
1483 	// Start with adding the header of original PDR
1484 	rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1485 				    new_record->data, new_record->size);
1486 	if (rc) {
1487 		goto cleanup_new_record_data;
1488 	}
1489 
1490 	pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle);
1491 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_version);
1492 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_type);
1493 	pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num);
1494 	// extract the header length from record and increment size with
1495 	// size of pldm_entity before inserting the value into new_record.
1496 	rc = pldm_msgbuf_extract(src, header_length);
1497 	if (rc) {
1498 		goto cleanup_new_record_data;
1499 	}
1500 	static_assert(UINT16_MAX < (SIZE_MAX - sizeof(pldm_entity)),
1501 		      "Fix the following bounds check.");
1502 	if (header_length + sizeof(pldm_entity) > UINT16_MAX) {
1503 		rc = -EOVERFLOW;
1504 		goto cleanup_new_record_data;
1505 	}
1506 	header_length += sizeof(pldm_entity);
1507 	pldm_msgbuf_insert(dst, header_length);
1508 	pldm_msgbuf_copy(dst, src, uint16_t, container_id);
1509 	pldm_msgbuf_copy(dst, src, uint8_t, association_type);
1510 	pldm_msgbuf_copy(dst, src, uint16_t, entity_type);
1511 	pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num);
1512 	pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id);
1513 	// extract value of number of children from record and increment it
1514 	// by 1 before insert the value to new record.
1515 	rc = pldm_msgbuf_extract(src, num_children);
1516 	if (rc) {
1517 		goto cleanup_new_record_data;
1518 	}
1519 	if (num_children == UINT8_MAX) {
1520 		rc = -EOVERFLOW;
1521 		goto cleanup_new_record_data;
1522 	}
1523 	num_children += 1;
1524 	pldm_msgbuf_insert(dst, num_children);
1525 	//Add all children of original PDR to new PDR
1526 	for (int i = 0; i < num_children - 1; i++) {
1527 		pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1528 		pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1529 		pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1530 	}
1531 
1532 	// Add new contained entity as a child of new PDR
1533 	rc = pldm_msgbuf_destroy(src);
1534 	if (rc) {
1535 		goto cleanup_new_record_data;
1536 	}
1537 	rc = pldm_msgbuf_init_errno(src, sizeof(struct pldm_entity), entity,
1538 				    sizeof(struct pldm_entity));
1539 	if (rc) {
1540 		goto cleanup_new_record_data;
1541 	}
1542 	pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1543 	pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1544 	pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1545 
1546 	rc = pldm_msgbuf_destroy(src);
1547 	if (rc) {
1548 		goto cleanup_new_record_data;
1549 	}
1550 	rc = pldm_msgbuf_destroy(dst);
1551 	if (rc) {
1552 		goto cleanup_new_record_data;
1553 	}
1554 
1555 	rc = pldm_pdr_replace_record(repo, record, prev, new_record);
1556 	if (rc) {
1557 		goto cleanup_new_record_data;
1558 	}
1559 
1560 	free(record->data);
1561 	free(record);
1562 	return rc;
1563 cleanup_new_record_data:
1564 	free(new_record->data);
1565 cleanup_new_record:
1566 	free(new_record);
1567 	return rc;
1568 }
1569 
1570 LIBPLDM_ABI_TESTING
1571 int pldm_entity_association_pdr_create_new(pldm_pdr *repo,
1572 					   uint32_t pdr_record_handle,
1573 					   pldm_entity *parent,
1574 					   pldm_entity *entity,
1575 					   uint32_t *entity_record_handle)
1576 {
1577 	if (!repo || !parent || !entity || !entity_record_handle) {
1578 		return -EINVAL;
1579 	}
1580 
1581 	if (pdr_record_handle == UINT32_MAX) {
1582 		return -EOVERFLOW;
1583 	}
1584 
1585 	bool pdr_added = false;
1586 	uint16_t new_pdr_size;
1587 	uint16_t container_id = 0;
1588 	void *container_id_addr;
1589 	struct pldm_msgbuf _dst;
1590 	struct pldm_msgbuf *dst = &_dst;
1591 	struct pldm_msgbuf _src_p;
1592 	struct pldm_msgbuf *src_p = &_src_p;
1593 	struct pldm_msgbuf _src_c;
1594 	struct pldm_msgbuf *src_c = &_src_c;
1595 	int rc = 0;
1596 
1597 	pldm_pdr_record *prev = repo->first;
1598 	pldm_pdr_record *record = repo->first;
1599 	pdr_added = pldm_pdr_find_record_by_handle(&record, &prev,
1600 						   pdr_record_handle);
1601 	if (!pdr_added) {
1602 		return -ENOENT;
1603 	}
1604 
1605 	static_assert(PDR_ENTITY_ASSOCIATION_MIN_SIZE < UINT16_MAX,
1606 		      "Truncation ahead");
1607 	new_pdr_size = PDR_ENTITY_ASSOCIATION_MIN_SIZE;
1608 	pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1609 	if (!new_record) {
1610 		return -ENOMEM;
1611 	}
1612 
1613 	new_record->data = malloc(new_pdr_size);
1614 	if (!new_record->data) {
1615 		rc = -ENOMEM;
1616 		goto cleanup_new_record;
1617 	}
1618 
1619 	// Initialise new PDR to be added with the header, size and handle.
1620 	// Set the position of new PDR
1621 	*entity_record_handle = pdr_record_handle + 1;
1622 	new_record->record_handle = *entity_record_handle;
1623 	new_record->size = new_pdr_size;
1624 	new_record->is_remote = false;
1625 
1626 	rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1627 				    new_record->data, new_record->size);
1628 	if (rc) {
1629 		goto cleanup_new_record_data;
1630 	}
1631 
1632 	// header record handle
1633 	pldm_msgbuf_insert(dst, *entity_record_handle);
1634 	// header version
1635 	pldm_msgbuf_insert_uint8(dst, 1);
1636 	// header type
1637 	pldm_msgbuf_insert_uint8(dst, PLDM_PDR_ENTITY_ASSOCIATION);
1638 	// header change number
1639 	pldm_msgbuf_insert_uint16(dst, 0);
1640 	// header length
1641 	pldm_msgbuf_insert_uint16(dst,
1642 				  (new_pdr_size - sizeof(struct pldm_pdr_hdr)));
1643 
1644 	// Data for new PDR is obtained from parent PDR and new contained entity
1645 	// is added as the child
1646 	rc = pldm_msgbuf_init_errno(src_p, sizeof(struct pldm_entity), parent,
1647 				    sizeof(*parent));
1648 	if (rc) {
1649 		goto cleanup_new_record_data;
1650 	}
1651 
1652 	rc = pldm_msgbuf_init_errno(src_c, sizeof(struct pldm_entity), entity,
1653 				    sizeof(*entity));
1654 	if (rc) {
1655 		goto cleanup_new_record_data;
1656 	}
1657 
1658 	container_id_addr = NULL;
1659 	// extract pointer for container ID and save the address
1660 	rc = pldm_msgbuf_span_required(dst, sizeof(container_id),
1661 				       (void **)&container_id_addr);
1662 	if (rc) {
1663 		goto cleanup_new_record_data;
1664 	}
1665 	assert(container_id_addr);
1666 	pldm_msgbuf_insert_uint8(dst, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
1667 	pldm_msgbuf_copy(dst, src_p, uint16_t, entity_type);
1668 	pldm_msgbuf_copy(dst, src_p, uint16_t, entity_instance_num);
1669 	pldm_msgbuf_copy(dst, src_p, uint16_t, entity_container_id);
1670 	// number of children
1671 	pldm_msgbuf_insert_uint8(dst, 1);
1672 
1673 	// Add new entity as child
1674 	pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_type);
1675 	pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_instance_num);
1676 	// Extract and insert child entity container ID and add same value to
1677 	// container ID of entity
1678 	pldm_msgbuf_extract(src_c, container_id);
1679 	pldm_msgbuf_insert(dst, container_id);
1680 	container_id = htole16(container_id);
1681 	memcpy(container_id_addr, &container_id, sizeof(uint16_t));
1682 
1683 	rc = pldm_msgbuf_destroy(dst);
1684 	if (rc) {
1685 		goto cleanup_new_record_data;
1686 	}
1687 	rc = pldm_msgbuf_destroy(src_p);
1688 	if (rc) {
1689 		goto cleanup_new_record_data;
1690 	}
1691 	rc = pldm_msgbuf_destroy(src_c);
1692 	if (rc) {
1693 		goto cleanup_new_record_data;
1694 	}
1695 
1696 	rc = pldm_pdr_insert_record(repo, record, new_record);
1697 	if (rc) {
1698 		goto cleanup_new_record_data;
1699 	}
1700 
1701 	return rc;
1702 cleanup_new_record_data:
1703 	free(new_record->data);
1704 cleanup_new_record:
1705 	free(new_record);
1706 	return rc;
1707 }
1708 
1709 LIBPLDM_CC_NONNULL
1710 static bool pldm_entity_cmp(const struct pldm_entity *l,
1711 			    const struct pldm_entity *r)
1712 {
1713 	return l->entity_type == r->entity_type &&
1714 	       l->entity_instance_num == r->entity_instance_num &&
1715 	       l->entity_container_id == r->entity_container_id;
1716 }
1717 
1718 /* Find record handle of a PDR record from PDR repo and
1719  * entity
1720  */
1721 LIBPLDM_CC_NONNULL
1722 static int pldm_entity_association_find_record_handle_by_entity(
1723 	pldm_pdr *repo, pldm_entity *entity, bool is_remote,
1724 	uint32_t *record_handle)
1725 {
1726 	uint8_t num_children = 0;
1727 	uint8_t hdr_type = 0;
1728 	int rc = 0;
1729 	size_t skip_data_size = 0;
1730 	pldm_pdr_record *record = repo->first;
1731 	struct pldm_msgbuf _dst;
1732 	struct pldm_msgbuf *dst = &_dst;
1733 
1734 	while (record != NULL) {
1735 		rc = pldm_msgbuf_init_errno(dst,
1736 					    PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1737 					    record->data, record->size);
1738 		if (rc) {
1739 			return rc;
1740 		}
1741 		skip_data_size = sizeof(uint32_t) + sizeof(uint8_t);
1742 		pldm_msgbuf_span_required(dst, skip_data_size, NULL);
1743 		pldm_msgbuf_extract(dst, hdr_type);
1744 		if (record->is_remote != is_remote ||
1745 		    hdr_type != PLDM_PDR_ENTITY_ASSOCIATION) {
1746 			goto cleanup;
1747 		}
1748 		skip_data_size = sizeof(uint16_t) + sizeof(uint16_t) +
1749 				 sizeof(uint16_t) + sizeof(uint8_t) +
1750 				 sizeof(struct pldm_entity);
1751 		pldm_msgbuf_span_required(dst, skip_data_size, NULL);
1752 		pldm_msgbuf_extract(dst, num_children);
1753 		for (int i = 0; i < num_children; ++i) {
1754 			struct pldm_entity e;
1755 
1756 			if ((rc = pldm_msgbuf_extract(dst, e.entity_type)) ||
1757 			    (rc = pldm_msgbuf_extract(dst,
1758 						      e.entity_instance_num)) ||
1759 			    (rc = pldm_msgbuf_extract(dst,
1760 						      e.entity_container_id))) {
1761 				return rc;
1762 			}
1763 
1764 			if (pldm_entity_cmp(entity, &e)) {
1765 				*record_handle = record->record_handle;
1766 				return 0;
1767 			}
1768 		}
1769 	cleanup:
1770 		rc = pldm_msgbuf_destroy(dst);
1771 		if (rc) {
1772 			return rc;
1773 		}
1774 		record = record->next;
1775 	}
1776 	return 0;
1777 }
1778 
1779 LIBPLDM_ABI_TESTING
1780 int pldm_entity_association_pdr_remove_contained_entity(
1781 	pldm_pdr *repo, pldm_entity *entity, bool is_remote,
1782 	uint32_t *pdr_record_handle)
1783 {
1784 	uint16_t header_length = 0;
1785 	uint8_t num_children = 0;
1786 	struct pldm_msgbuf _src;
1787 	struct pldm_msgbuf *src = &_src;
1788 	struct pldm_msgbuf _dst;
1789 	struct pldm_msgbuf *dst = &_dst;
1790 	int rc;
1791 	pldm_pdr_record *record;
1792 	pldm_pdr_record *prev;
1793 
1794 	if (!repo || !entity || !pdr_record_handle) {
1795 		return -EINVAL;
1796 	}
1797 	record = repo->first;
1798 	prev = repo->first;
1799 
1800 	rc = pldm_entity_association_find_record_handle_by_entity(
1801 		repo, entity, is_remote, pdr_record_handle);
1802 	if (rc) {
1803 		return rc;
1804 	}
1805 	pldm_pdr_find_record_by_handle(&record, &prev, *pdr_record_handle);
1806 	if (!record) {
1807 		return -EINVAL;
1808 	}
1809 	// Initialize msg buffer for record and record->data
1810 	rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1811 				    record->data, record->size);
1812 	if (rc) {
1813 		return rc;
1814 	}
1815 	// check if removing an entity from record causes overflow before
1816 	// allocating memory for new_record.
1817 	if (record->size < sizeof(pldm_entity)) {
1818 		return -EOVERFLOW;
1819 	}
1820 	pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1821 	if (!new_record) {
1822 		return -ENOMEM;
1823 	}
1824 	new_record->data = malloc(record->size - sizeof(pldm_entity));
1825 	if (!new_record->data) {
1826 		rc = -ENOMEM;
1827 		goto cleanup_new_record;
1828 	}
1829 	new_record->record_handle = record->record_handle;
1830 	new_record->size = record->size - sizeof(struct pldm_entity);
1831 	new_record->is_remote = record->is_remote;
1832 
1833 	// Initialize new PDR record with data from original PDR record.
1834 	// Start with adding the header of original PDR
1835 	rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1836 				    new_record->data, new_record->size);
1837 	if (rc) {
1838 		goto cleanup_new_record_data;
1839 	}
1840 	pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle);
1841 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_version);
1842 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_type);
1843 	pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num);
1844 	// extract the header length from record and decrement size with
1845 	// size of pldm_entity before inserting the value into new_record.
1846 	rc = pldm_msgbuf_extract(src, header_length);
1847 	if (rc) {
1848 		goto cleanup_new_record_data;
1849 	}
1850 	if (header_length < sizeof(pldm_entity)) {
1851 		rc = -EOVERFLOW;
1852 		goto cleanup_new_record_data;
1853 	}
1854 	header_length -= sizeof(pldm_entity);
1855 	pldm_msgbuf_insert(dst, header_length);
1856 	pldm_msgbuf_copy(dst, src, uint16_t, container_id);
1857 	pldm_msgbuf_copy(dst, src, uint8_t, association_type);
1858 	pldm_msgbuf_copy(dst, src, uint16_t, entity_type);
1859 	pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num);
1860 	pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id);
1861 	// extract value of number of children from record and decrement it
1862 	// by 1 before insert the value to new record.
1863 	rc = pldm_msgbuf_extract(src, num_children);
1864 	if (rc) {
1865 		goto cleanup_new_record_data;
1866 	}
1867 	if (num_children == 1) {
1868 		prev->next = record->next;
1869 		free(record->data);
1870 		free(record);
1871 		goto cleanup_new_record_data;
1872 	} else if (num_children < 1) {
1873 		rc = -EOVERFLOW;
1874 		goto cleanup_new_record_data;
1875 	}
1876 	num_children -= 1;
1877 	pldm_msgbuf_insert(dst, num_children);
1878 	//Add all children of original PDR to new PDR
1879 	for (int i = 0; i < num_children + 1; ++i) {
1880 		struct pldm_entity e;
1881 
1882 		if ((rc = pldm_msgbuf_extract(src, e.entity_type)) ||
1883 		    (rc = pldm_msgbuf_extract(src, e.entity_instance_num)) ||
1884 		    (rc = pldm_msgbuf_extract(src, e.entity_container_id))) {
1885 			goto cleanup_new_record_data;
1886 		}
1887 
1888 		if (pldm_entity_cmp(entity, &e)) {
1889 			continue;
1890 		}
1891 
1892 		pldm_msgbuf_insert(dst, e.entity_type);
1893 		pldm_msgbuf_insert(dst, e.entity_instance_num);
1894 		pldm_msgbuf_insert(dst, e.entity_container_id);
1895 	}
1896 
1897 	if ((rc = pldm_msgbuf_destroy(src)) ||
1898 	    (rc = pldm_msgbuf_destroy(dst)) ||
1899 	    (rc = pldm_pdr_replace_record(repo, record, prev, new_record))) {
1900 		goto cleanup_new_record_data;
1901 	}
1902 
1903 	free(record->data);
1904 	free(record);
1905 	return rc;
1906 
1907 cleanup_new_record_data:
1908 	free(new_record->data);
1909 cleanup_new_record:
1910 	free(new_record);
1911 	return rc;
1912 }
1913 
1914 /* API to find the PDR record that is previous to a given PLDM PDR
1915  * record in a given PLDM PDR repository
1916  */
1917 LIBPLDM_CC_NONNULL
1918 static pldm_pdr_record *pldm_pdr_get_prev_record(pldm_pdr *repo,
1919 						 pldm_pdr_record *record)
1920 {
1921 	pldm_pdr_record *prev = NULL;
1922 	pldm_pdr_record *curr = repo->first;
1923 
1924 	while (curr != NULL) {
1925 		if (curr->record_handle == record->record_handle) {
1926 			break;
1927 		}
1928 		prev = curr;
1929 		curr = curr->next;
1930 	}
1931 	return prev;
1932 }
1933 
1934 /* API to check if a PLDM PDR record is present in a PLDM PDR repository
1935  */
1936 LIBPLDM_CC_NONNULL
1937 static bool is_prev_record_present(pldm_pdr *repo, pldm_pdr_record *record)
1938 {
1939 	if (repo->first == record) {
1940 		return true;
1941 	}
1942 
1943 	return pldm_pdr_get_prev_record(repo, record) != NULL;
1944 }
1945 
1946 /* API to check if FRU RSI of record matches the given record set identifier.
1947  * Returns 1 if the provided FRU record matches the provided record set identifier,
1948  * 0 if it does not, otherwise -EINVAL if the arguments are invalid.
1949  */
1950 LIBPLDM_CC_NONNULL
1951 static int pldm_pdr_record_matches_fru_rsi(const pldm_pdr_record *record,
1952 					   uint16_t rsi)
1953 {
1954 	uint16_t record_fru_rsi = 0;
1955 	uint8_t *skip_data = NULL;
1956 	uint8_t skip_data_size = 0;
1957 	struct pldm_msgbuf _dst;
1958 	struct pldm_msgbuf *dst = &_dst;
1959 	int rc = 0;
1960 
1961 	rc = pldm_msgbuf_init_errno(dst, PDR_FRU_RECORD_SET_MIN_SIZE,
1962 				    record->data, record->size);
1963 	if (rc) {
1964 		return rc;
1965 	}
1966 	skip_data_size = sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t);
1967 	pldm_msgbuf_span_required(dst, skip_data_size, (void **)&skip_data);
1968 	pldm_msgbuf_extract(dst, record_fru_rsi);
1969 
1970 	rc = pldm_msgbuf_destroy(dst);
1971 	if (rc) {
1972 		return rc;
1973 	}
1974 	return record_fru_rsi == rsi;
1975 }
1976 
1977 /* API to remove PLDM PDR record from a PLDM PDR repository
1978  */
1979 LIBPLDM_CC_NONNULL_ARGS(1, 2)
1980 static int pldm_pdr_remove_record(pldm_pdr *repo, pldm_pdr_record *record,
1981 				  pldm_pdr_record *prev)
1982 {
1983 	if (!is_prev_record_present(repo, record)) {
1984 		return -EINVAL;
1985 	}
1986 
1987 	assert(repo->size >= record->size);
1988 	if (repo->size < record->size) {
1989 		return -EOVERFLOW;
1990 	}
1991 
1992 	if (repo->first == record) {
1993 		repo->first = record->next;
1994 	} else {
1995 		if (prev != NULL) {
1996 			prev->next = record->next;
1997 		}
1998 	}
1999 
2000 	if (repo->last == record) {
2001 		repo->last = prev;
2002 		if (prev != NULL) {
2003 			prev->next = NULL;
2004 		}
2005 	}
2006 	repo->record_count -= 1;
2007 	repo->size -= record->size;
2008 	free(record->data);
2009 	free(record);
2010 
2011 	return 0;
2012 }
2013 
2014 LIBPLDM_ABI_TESTING
2015 int pldm_pdr_remove_fru_record_set_by_rsi(pldm_pdr *repo, uint16_t fru_rsi,
2016 					  bool is_remote,
2017 					  uint32_t *record_handle)
2018 {
2019 	pldm_pdr_record *record;
2020 	pldm_pdr_record *prev = NULL;
2021 	size_t skip_data_size = sizeof(uint32_t) + sizeof(uint8_t);
2022 	uint8_t hdr_type = 0;
2023 	int rc = 0;
2024 	int match;
2025 
2026 	if (!repo || !record_handle) {
2027 		return -EINVAL;
2028 	}
2029 	record = repo->first;
2030 
2031 	while (record != NULL) {
2032 		struct pldm_msgbuf _buf;
2033 		struct pldm_msgbuf *buf = &_buf;
2034 		rc = pldm_msgbuf_init_errno(buf, PDR_FRU_RECORD_SET_MIN_SIZE,
2035 					    record->data, record->size);
2036 		if (rc) {
2037 			return rc;
2038 		}
2039 		pldm_msgbuf_span_required(buf, skip_data_size, NULL);
2040 		if ((rc = pldm_msgbuf_extract(buf, hdr_type))) {
2041 			return rc;
2042 		}
2043 		if (record->is_remote != is_remote ||
2044 		    hdr_type != PLDM_PDR_FRU_RECORD_SET) {
2045 			goto cleanup;
2046 		}
2047 		match = pldm_pdr_record_matches_fru_rsi(record, fru_rsi);
2048 		if (match < 0) {
2049 			return match;
2050 		}
2051 		if (match) {
2052 			*record_handle = record->record_handle;
2053 			prev = pldm_pdr_get_prev_record(repo, record);
2054 			return pldm_pdr_remove_record(repo, record, prev);
2055 		}
2056 	cleanup:
2057 		rc = pldm_msgbuf_destroy(buf);
2058 		if (rc) {
2059 			return rc;
2060 		}
2061 		record = record->next;
2062 	}
2063 	return rc;
2064 }
2065