xref: /openbmc/libpldm/src/dsp/pdr.c (revision 5befd128)
1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
2 #include "msgbuf.h"
3 #include <libpldm/pdr.h>
4 #include <libpldm/platform.h>
5 
6 #include <assert.h>
7 #include <endian.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <errno.h>
11 
12 #define PDR_ENTITY_ASSOCIATION_MIN_SIZE                                        \
13 	(sizeof(struct pldm_pdr_hdr) +                                         \
14 	 sizeof(struct pldm_pdr_entity_association))
15 
16 typedef struct pldm_pdr_record {
17 	uint32_t record_handle;
18 	uint32_t size;
19 	uint8_t *data;
20 	struct pldm_pdr_record *next;
21 	bool is_remote;
22 	uint16_t terminus_handle;
23 } pldm_pdr_record;
24 
25 typedef struct pldm_pdr {
26 	uint32_t record_count;
27 	uint32_t size;
28 	pldm_pdr_record *first;
29 	pldm_pdr_record *last;
30 } pldm_pdr;
31 
32 static inline uint32_t get_next_record_handle(const pldm_pdr *repo,
33 					      const pldm_pdr_record *record)
34 {
35 	assert(repo != NULL);
36 	assert(record != NULL);
37 
38 	if (record == repo->last) {
39 		return 0;
40 	}
41 	return record->next->record_handle;
42 }
43 
44 LIBPLDM_ABI_STABLE
45 int pldm_pdr_add_check(pldm_pdr *repo, const uint8_t *data, uint32_t size,
46 		       bool is_remote, uint16_t terminus_handle,
47 		       uint32_t *record_handle)
48 {
49 	uint32_t curr;
50 
51 	if (!repo || !data || !size) {
52 		return -EINVAL;
53 	}
54 
55 	if (record_handle && *record_handle) {
56 		curr = *record_handle;
57 	} else if (repo->last) {
58 		curr = repo->last->record_handle;
59 		if (curr == UINT32_MAX) {
60 			return -EOVERFLOW;
61 		}
62 		curr += 1;
63 	} else {
64 		curr = 1;
65 	}
66 
67 	pldm_pdr_record *record = malloc(sizeof(pldm_pdr_record));
68 	if (!record) {
69 		return -ENOMEM;
70 	}
71 
72 	if (data) {
73 		record->data = malloc(size);
74 		if (!record->data) {
75 			free(record);
76 			return -ENOMEM;
77 		}
78 		memcpy(record->data, data, size);
79 	}
80 
81 	record->size = size;
82 	record->is_remote = is_remote;
83 	record->terminus_handle = terminus_handle;
84 	record->record_handle = curr;
85 
86 	if (record_handle && !*record_handle && data) {
87 		/* If record handle is 0, that is an indication for this API to
88 		 * compute a new handle. For that reason, the computed handle
89 		 * needs to be populated in the PDR header. For a case where the
90 		 * caller supplied the record handle, it would exist in the
91 		 * header already.
92 		 */
93 		struct pldm_pdr_hdr *hdr = (void *)record->data;
94 		hdr->record_handle = htole32(record->record_handle);
95 	}
96 
97 	record->next = NULL;
98 
99 	assert(!repo->first == !repo->last);
100 	if (repo->first == NULL) {
101 		repo->first = record;
102 		repo->last = record;
103 	} else {
104 		repo->last->next = record;
105 		repo->last = record;
106 	}
107 
108 	repo->size += record->size;
109 	++repo->record_count;
110 
111 	if (record_handle) {
112 		*record_handle = record->record_handle;
113 	}
114 
115 	return 0;
116 }
117 
118 LIBPLDM_ABI_STABLE
119 pldm_pdr *pldm_pdr_init(void)
120 {
121 	pldm_pdr *repo = malloc(sizeof(pldm_pdr));
122 	if (!repo) {
123 		return NULL;
124 	}
125 	repo->record_count = 0;
126 	repo->size = 0;
127 	repo->first = NULL;
128 	repo->last = NULL;
129 
130 	return repo;
131 }
132 
133 LIBPLDM_ABI_STABLE
134 void pldm_pdr_destroy(pldm_pdr *repo)
135 {
136 	if (!repo) {
137 		return;
138 	}
139 
140 	pldm_pdr_record *record = repo->first;
141 	while (record != NULL) {
142 		pldm_pdr_record *next = record->next;
143 		if (record->data) {
144 			free(record->data);
145 			record->data = NULL;
146 		}
147 		free(record);
148 		record = next;
149 	}
150 	free(repo);
151 }
152 
153 LIBPLDM_ABI_STABLE
154 const pldm_pdr_record *pldm_pdr_find_record(const pldm_pdr *repo,
155 					    uint32_t record_handle,
156 					    uint8_t **data, uint32_t *size,
157 					    uint32_t *next_record_handle)
158 {
159 	if (!repo || !data || !size || !next_record_handle) {
160 		return NULL;
161 	}
162 
163 	if (!record_handle && (repo->first != NULL)) {
164 		record_handle = repo->first->record_handle;
165 	}
166 
167 	pldm_pdr_record *record = repo->first;
168 	while (record != NULL) {
169 		if (record->record_handle == record_handle) {
170 			*size = record->size;
171 			*data = record->data;
172 			*next_record_handle =
173 				get_next_record_handle(repo, record);
174 			return record;
175 		}
176 		record = record->next;
177 	}
178 
179 	*size = 0;
180 	*next_record_handle = 0;
181 	return NULL;
182 }
183 
184 LIBPLDM_ABI_STABLE
185 const pldm_pdr_record *
186 pldm_pdr_get_next_record(const pldm_pdr *repo,
187 			 const pldm_pdr_record *curr_record, uint8_t **data,
188 			 uint32_t *size, uint32_t *next_record_handle)
189 {
190 	if (!repo || !curr_record || !data || !size || !next_record_handle) {
191 		return NULL;
192 	}
193 
194 	if (curr_record == repo->last) {
195 		*data = NULL;
196 		*size = 0;
197 		*next_record_handle = get_next_record_handle(repo, curr_record);
198 		return NULL;
199 	}
200 
201 	*next_record_handle = get_next_record_handle(repo, curr_record->next);
202 	*data = curr_record->next->data;
203 	*size = curr_record->next->size;
204 	return curr_record->next;
205 }
206 
207 LIBPLDM_ABI_STABLE
208 const pldm_pdr_record *
209 pldm_pdr_find_record_by_type(const pldm_pdr *repo, uint8_t pdr_type,
210 			     const pldm_pdr_record *curr_record, uint8_t **data,
211 			     uint32_t *size)
212 {
213 	if (!repo) {
214 		return NULL;
215 	}
216 
217 	pldm_pdr_record *record = repo->first;
218 	if (curr_record != NULL) {
219 		record = curr_record->next;
220 	}
221 	while (record != NULL) {
222 		struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)record->data;
223 		if (hdr->type == pdr_type) {
224 			if (data && size) {
225 				*size = record->size;
226 				*data = record->data;
227 			}
228 			return record;
229 		}
230 		record = record->next;
231 	}
232 
233 	if (size) {
234 		*size = 0;
235 	}
236 	return NULL;
237 }
238 
239 LIBPLDM_ABI_STABLE
240 uint32_t pldm_pdr_get_record_count(const pldm_pdr *repo)
241 {
242 	assert(repo != NULL);
243 
244 	return repo->record_count;
245 }
246 
247 LIBPLDM_ABI_STABLE
248 uint32_t pldm_pdr_get_repo_size(const pldm_pdr *repo)
249 {
250 	assert(repo != NULL);
251 
252 	return repo->size;
253 }
254 
255 LIBPLDM_ABI_STABLE
256 uint32_t pldm_pdr_get_record_handle(const pldm_pdr *repo
257 				    __attribute__((unused)),
258 				    const pldm_pdr_record *record)
259 {
260 	assert(repo != NULL);
261 	assert(record != NULL);
262 
263 	return record->record_handle;
264 }
265 
266 LIBPLDM_ABI_TESTING
267 uint16_t pldm_pdr_get_terminus_handle(const pldm_pdr *repo
268 				      __attribute__((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_check(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 	uint32_t size = sizeof(struct pldm_pdr_hdr) +
297 			sizeof(struct pldm_pdr_fru_record_set);
298 	uint8_t data[size];
299 
300 	struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)&data;
301 	hdr->version = 1;
302 	hdr->record_handle = *bmc_record_handle;
303 	hdr->type = PLDM_PDR_FRU_RECORD_SET;
304 	hdr->record_change_num = 0;
305 	hdr->length = htole16(sizeof(struct pldm_pdr_fru_record_set));
306 	struct pldm_pdr_fru_record_set *fru =
307 		(struct pldm_pdr_fru_record_set *)((uint8_t *)hdr +
308 						   sizeof(struct pldm_pdr_hdr));
309 	fru->terminus_handle = htole16(terminus_handle);
310 	fru->fru_rsi = htole16(fru_rsi);
311 	fru->entity_type = htole16(entity_type);
312 	fru->entity_instance_num = htole16(entity_instance_num);
313 	fru->container_id = htole16(container_id);
314 
315 	return pldm_pdr_add_check(repo, data, size, false, terminus_handle,
316 				  bmc_record_handle);
317 }
318 
319 LIBPLDM_ABI_STABLE
320 const pldm_pdr_record *pldm_pdr_fru_record_set_find_by_rsi(
321 	const pldm_pdr *repo, uint16_t fru_rsi, uint16_t *terminus_handle,
322 	uint16_t *entity_type, uint16_t *entity_instance_num,
323 	uint16_t *container_id)
324 {
325 	if (!repo || !terminus_handle || !entity_type || !entity_instance_num ||
326 	    !container_id) {
327 		return NULL;
328 	}
329 
330 	uint8_t *data = NULL;
331 	uint32_t size = 0;
332 	const pldm_pdr_record *curr_record = pldm_pdr_find_record_by_type(
333 		repo, PLDM_PDR_FRU_RECORD_SET, NULL, &data, &size);
334 	while (curr_record != NULL) {
335 		struct pldm_pdr_fru_record_set *fru =
336 			(struct pldm_pdr_fru_record_set
337 				 *)(data + sizeof(struct pldm_pdr_hdr));
338 		if (fru->fru_rsi == htole16(fru_rsi)) {
339 			*terminus_handle = le16toh(fru->terminus_handle);
340 			*entity_type = le16toh(fru->entity_type);
341 			*entity_instance_num =
342 				le16toh(fru->entity_instance_num);
343 			*container_id = le16toh(fru->container_id);
344 			return curr_record;
345 		}
346 		data = NULL;
347 		curr_record = pldm_pdr_find_record_by_type(
348 			repo, PLDM_PDR_FRU_RECORD_SET, curr_record, &data,
349 			&size);
350 	}
351 
352 	*terminus_handle = 0;
353 	*entity_type = 0;
354 	*entity_instance_num = 0;
355 	*container_id = 0;
356 
357 	return NULL;
358 }
359 
360 LIBPLDM_ABI_STABLE
361 /* NOLINTNEXTLINE(readability-identifier-naming) */
362 void pldm_pdr_update_TL_pdr(const pldm_pdr *repo, uint16_t terminus_handle,
363 			    uint8_t tid, uint8_t tl_eid, bool valid_bit)
364 {
365 	uint8_t *out_data = NULL;
366 	uint32_t size = 0;
367 	const pldm_pdr_record *record;
368 	record = pldm_pdr_find_record_by_type(repo, PLDM_TERMINUS_LOCATOR_PDR,
369 					      NULL, &out_data, &size);
370 
371 	do {
372 		if (record != NULL) {
373 			struct pldm_terminus_locator_pdr *pdr =
374 				(struct pldm_terminus_locator_pdr *)out_data;
375 			struct pldm_terminus_locator_type_mctp_eid *value =
376 				(struct pldm_terminus_locator_type_mctp_eid *)
377 					pdr->terminus_locator_value;
378 			if (pdr->terminus_handle == terminus_handle &&
379 			    pdr->tid == tid && value->eid == tl_eid) {
380 				pdr->validity = valid_bit;
381 				break;
382 			}
383 		}
384 		record = pldm_pdr_find_record_by_type(repo,
385 						      PLDM_TERMINUS_LOCATOR_PDR,
386 						      record, &out_data, &size);
387 	} while (record);
388 }
389 
390 static bool pldm_record_handle_in_range(uint32_t record_handle,
391 					uint32_t first_record_handle,
392 					uint32_t last_record_handle)
393 {
394 	return record_handle >= first_record_handle &&
395 	       record_handle <= last_record_handle;
396 }
397 
398 LIBPLDM_ABI_TESTING
399 int pldm_pdr_find_child_container_id_index_range_exclude(
400 	const pldm_pdr *repo, uint16_t entity_type, uint16_t entity_instance,
401 	uint8_t child_index, uint32_t range_exclude_start_handle,
402 	uint32_t range_exclude_end_handle, uint16_t *container_id)
403 {
404 	pldm_pdr_record *record;
405 	if (!repo) {
406 		return -EINVAL;
407 	}
408 
409 	for (record = repo->first; record; record = record->next) {
410 		bool is_container_entity_instance_number;
411 		struct pldm_pdr_entity_association *pdr;
412 		bool is_container_entity_type;
413 		struct pldm_entity *child;
414 		struct pldm_pdr_hdr *hdr;
415 		bool in_range;
416 
417 		// pldm_pdr_add() takes only uint8_t* data as an argument.
418 		// The expectation here is the pldm_pdr_hdr is the first field of the record data
419 		hdr = (struct pldm_pdr_hdr *)record->data;
420 		if (hdr->type != PLDM_PDR_ENTITY_ASSOCIATION) {
421 			continue;
422 		}
423 		in_range = pldm_record_handle_in_range(
424 			record->record_handle, range_exclude_start_handle,
425 			range_exclude_end_handle);
426 		if (in_range) {
427 			continue;
428 		}
429 
430 		// this cast is valid with respect to alignment because
431 		// struct pldm_pdr_hdr is declared with __attribute__((packed))
432 		pdr = (void *)(record->data + sizeof(struct pldm_pdr_hdr));
433 		if (child_index >= pdr->num_children) {
434 			continue;
435 		}
436 
437 		child = (&pdr->children[child_index]);
438 		is_container_entity_type = pdr->container.entity_type ==
439 					   entity_type;
440 		is_container_entity_instance_number =
441 			pdr->container.entity_instance_num == entity_instance;
442 		if (is_container_entity_type &&
443 		    is_container_entity_instance_number) {
444 			*container_id = le16toh(child->entity_container_id);
445 			return 0;
446 		}
447 	}
448 	return -ENOKEY;
449 }
450 
451 typedef struct pldm_entity_association_tree {
452 	pldm_entity_node *root;
453 	uint16_t last_used_container_id;
454 } pldm_entity_association_tree;
455 
456 typedef struct pldm_entity_node {
457 	pldm_entity entity;
458 	pldm_entity parent;
459 	uint16_t remote_container_id;
460 	pldm_entity_node *first_child;
461 	pldm_entity_node *next_sibling;
462 	uint8_t association_type;
463 } pldm_entity_node;
464 
465 static inline uint16_t next_container_id(pldm_entity_association_tree *tree)
466 {
467 	assert(tree != NULL);
468 	assert(tree->last_used_container_id != UINT16_MAX);
469 
470 	return ++tree->last_used_container_id;
471 }
472 
473 LIBPLDM_ABI_STABLE
474 pldm_entity pldm_entity_extract(pldm_entity_node *node)
475 {
476 	assert(node != NULL);
477 
478 	return node->entity;
479 }
480 
481 LIBPLDM_ABI_STABLE
482 uint16_t
483 pldm_entity_node_get_remote_container_id(const pldm_entity_node *entity)
484 {
485 	assert(entity != NULL);
486 
487 	return entity->remote_container_id;
488 }
489 
490 LIBPLDM_ABI_STABLE
491 pldm_entity_association_tree *pldm_entity_association_tree_init(void)
492 {
493 	pldm_entity_association_tree *tree =
494 		malloc(sizeof(pldm_entity_association_tree));
495 	if (!tree) {
496 		return NULL;
497 	}
498 	tree->root = NULL;
499 	tree->last_used_container_id = 0;
500 
501 	return tree;
502 }
503 
504 static pldm_entity_node *find_insertion_at(pldm_entity_node *start,
505 					   uint16_t entity_type)
506 {
507 	assert(start != NULL);
508 
509 	/* Insert after the the last node that matches the input entity type, or
510 	 * at the end if no such match occurs
511 	 */
512 	while (start->next_sibling != NULL) {
513 		uint16_t this_type = start->entity.entity_type;
514 		pldm_entity_node *next = start->next_sibling;
515 		if (this_type == entity_type &&
516 		    (this_type != next->entity.entity_type)) {
517 			break;
518 		}
519 		start = start->next_sibling;
520 	}
521 
522 	return start;
523 }
524 
525 LIBPLDM_ABI_STABLE
526 pldm_entity_node *pldm_entity_association_tree_add(
527 	pldm_entity_association_tree *tree, pldm_entity *entity,
528 	uint16_t entity_instance_number, pldm_entity_node *parent,
529 	uint8_t association_type)
530 {
531 	return pldm_entity_association_tree_add_entity(tree, entity,
532 						       entity_instance_number,
533 						       parent, association_type,
534 						       false, true, 0xffff);
535 }
536 
537 LIBPLDM_ABI_STABLE
538 pldm_entity_node *pldm_entity_association_tree_add_entity(
539 	pldm_entity_association_tree *tree, pldm_entity *entity,
540 	uint16_t entity_instance_number, pldm_entity_node *parent,
541 	uint8_t association_type, bool is_remote, bool is_update_container_id,
542 	uint16_t container_id)
543 {
544 	if ((!tree) || (!entity)) {
545 		return NULL;
546 	}
547 
548 	if (entity_instance_number != 0xffff && parent != NULL) {
549 		pldm_entity node;
550 		node.entity_type = entity->entity_type;
551 		node.entity_instance_num = entity_instance_number;
552 		if (pldm_is_current_parent_child(parent, &node)) {
553 			return NULL;
554 		}
555 	}
556 	if (association_type != PLDM_ENTITY_ASSOCIAION_PHYSICAL &&
557 	    association_type != PLDM_ENTITY_ASSOCIAION_LOGICAL) {
558 		return NULL;
559 	}
560 	pldm_entity_node *node = malloc(sizeof(pldm_entity_node));
561 	if (!node) {
562 		return NULL;
563 	}
564 	node->first_child = NULL;
565 	node->next_sibling = NULL;
566 	node->parent.entity_type = 0;
567 	node->parent.entity_instance_num = 0;
568 	node->parent.entity_container_id = 0;
569 	node->entity.entity_type = entity->entity_type;
570 	node->entity.entity_instance_num =
571 		entity_instance_number != 0xffff ? entity_instance_number : 1;
572 	node->association_type = association_type;
573 	node->remote_container_id = 0;
574 	if (tree->root == NULL) {
575 		if (parent != NULL) {
576 			free(node);
577 			return NULL;
578 		}
579 		tree->root = node;
580 		/* container_id 0 here indicates this is the top-most entry */
581 		node->entity.entity_container_id = 0;
582 		node->remote_container_id = node->entity.entity_container_id;
583 	} else if (parent != NULL && parent->first_child == NULL) {
584 		/* Ensure next_container_id() will yield a valid ID */
585 		if (tree->last_used_container_id == UINT16_MAX) {
586 			free(node);
587 			return NULL;
588 		}
589 
590 		parent->first_child = node;
591 		node->parent = parent->entity;
592 
593 		if (is_remote) {
594 			node->remote_container_id = entity->entity_container_id;
595 		}
596 		if (is_update_container_id) {
597 			if (container_id != 0xffff) {
598 				node->entity.entity_container_id = container_id;
599 			} else {
600 				node->entity.entity_container_id =
601 					next_container_id(tree);
602 			}
603 		} else {
604 			node->entity.entity_container_id =
605 				entity->entity_container_id;
606 		}
607 
608 		if (!is_remote) {
609 			node->remote_container_id =
610 				node->entity.entity_container_id;
611 		}
612 	} else {
613 		pldm_entity_node *start = parent == NULL ? tree->root :
614 							   parent->first_child;
615 		pldm_entity_node *prev =
616 			find_insertion_at(start, entity->entity_type);
617 		if (!prev) {
618 			free(node);
619 			return NULL;
620 		}
621 		pldm_entity_node *next = prev->next_sibling;
622 		if (prev->entity.entity_type == entity->entity_type) {
623 			if (prev->entity.entity_instance_num == UINT16_MAX) {
624 				free(node);
625 				return NULL;
626 			}
627 			node->entity.entity_instance_num =
628 				entity_instance_number != 0xffff ?
629 					entity_instance_number :
630 					prev->entity.entity_instance_num + 1;
631 		}
632 		prev->next_sibling = node;
633 		node->parent = prev->parent;
634 		node->next_sibling = next;
635 		node->entity.entity_container_id =
636 			prev->entity.entity_container_id;
637 		node->remote_container_id = entity->entity_container_id;
638 	}
639 	entity->entity_instance_num = node->entity.entity_instance_num;
640 	if (is_update_container_id) {
641 		entity->entity_container_id = node->entity.entity_container_id;
642 	}
643 	return node;
644 }
645 
646 static void get_num_nodes(pldm_entity_node *node, size_t *num)
647 {
648 	if (node == NULL) {
649 		return;
650 	}
651 
652 	++(*num);
653 	get_num_nodes(node->next_sibling, num);
654 	get_num_nodes(node->first_child, num);
655 }
656 
657 static void entity_association_tree_visit(pldm_entity_node *node,
658 					  pldm_entity *entities, size_t *index)
659 {
660 	if (node == NULL) {
661 		return;
662 	}
663 
664 	pldm_entity *entity = &entities[*index];
665 	++(*index);
666 	entity->entity_type = node->entity.entity_type;
667 	entity->entity_instance_num = node->entity.entity_instance_num;
668 	entity->entity_container_id = node->entity.entity_container_id;
669 
670 	entity_association_tree_visit(node->next_sibling, entities, index);
671 	entity_association_tree_visit(node->first_child, entities, index);
672 }
673 
674 LIBPLDM_ABI_STABLE
675 void pldm_entity_association_tree_visit(pldm_entity_association_tree *tree,
676 					pldm_entity **entities, size_t *size)
677 {
678 	if (!tree || !entities || !size) {
679 		return;
680 	}
681 
682 	*size = 0;
683 	if (tree->root == NULL) {
684 		return;
685 	}
686 
687 	get_num_nodes(tree->root, size);
688 	*entities = malloc(*size * sizeof(pldm_entity));
689 	if (!entities) {
690 		return;
691 	}
692 	size_t index = 0;
693 	entity_association_tree_visit(tree->root, *entities, &index);
694 }
695 
696 static void entity_association_tree_destroy(pldm_entity_node *node)
697 {
698 	if (node == NULL) {
699 		return;
700 	}
701 
702 	entity_association_tree_destroy(node->next_sibling);
703 	entity_association_tree_destroy(node->first_child);
704 	free(node);
705 }
706 
707 LIBPLDM_ABI_STABLE
708 void pldm_entity_association_tree_destroy(pldm_entity_association_tree *tree)
709 {
710 	if (!tree) {
711 		return;
712 	}
713 
714 	entity_association_tree_destroy(tree->root);
715 	free(tree);
716 }
717 
718 LIBPLDM_ABI_STABLE
719 bool pldm_entity_is_node_parent(pldm_entity_node *node)
720 {
721 	assert(node != NULL);
722 
723 	return node->first_child != NULL;
724 }
725 
726 LIBPLDM_ABI_STABLE
727 pldm_entity pldm_entity_get_parent(pldm_entity_node *node)
728 {
729 	assert(node != NULL);
730 
731 	return node->parent;
732 }
733 
734 LIBPLDM_ABI_STABLE
735 bool pldm_entity_is_exist_parent(pldm_entity_node *node)
736 {
737 	assert(node != NULL);
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 pdr[size];
800 	uint8_t *start = pdr;
801 
802 	struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)start;
803 	hdr->version = 1;
804 	hdr->record_handle = record_handle;
805 	hdr->type = PLDM_PDR_ENTITY_ASSOCIATION;
806 	hdr->record_change_num = 0;
807 	hdr->length = htole16(size - sizeof(struct pldm_pdr_hdr));
808 	start += sizeof(struct pldm_pdr_hdr);
809 
810 	uint16_t *container_id = (uint16_t *)start;
811 	*container_id = htole16(curr->first_child->entity.entity_container_id);
812 	start += sizeof(uint16_t);
813 	*start = association_type;
814 	start += sizeof(uint8_t);
815 
816 	pldm_entity *entity = (pldm_entity *)start;
817 	entity->entity_type = htole16(curr->entity.entity_type);
818 	entity->entity_instance_num = htole16(curr->entity.entity_instance_num);
819 	entity->entity_container_id = htole16(curr->entity.entity_container_id);
820 	start += sizeof(pldm_entity);
821 
822 	*start = contained_count;
823 	start += sizeof(uint8_t);
824 
825 	pldm_entity_node *node = curr->first_child;
826 	while (node != NULL) {
827 		if (node->association_type == association_type) {
828 			pldm_entity *entity = (pldm_entity *)start;
829 			entity->entity_type = htole16(node->entity.entity_type);
830 			entity->entity_instance_num =
831 				htole16(node->entity.entity_instance_num);
832 			entity->entity_container_id =
833 				htole16(node->entity.entity_container_id);
834 			start += sizeof(pldm_entity);
835 		}
836 		node = node->next_sibling;
837 	}
838 
839 	return pldm_pdr_add_check(repo, pdr, size, is_remote, terminus_handle,
840 				  &record_handle);
841 }
842 
843 static int entity_association_pdr_add_entry(pldm_entity_node *curr,
844 					    pldm_pdr *repo, bool is_remote,
845 					    uint16_t terminus_handle,
846 					    uint32_t record_handle)
847 {
848 	uint8_t num_logical_children = pldm_entity_get_num_children(
849 		curr, PLDM_ENTITY_ASSOCIAION_LOGICAL);
850 	uint8_t num_physical_children = pldm_entity_get_num_children(
851 		curr, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
852 	int rc;
853 
854 	if (num_logical_children) {
855 		uint16_t logical_pdr_size =
856 			sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
857 			sizeof(uint8_t) + sizeof(pldm_entity) +
858 			sizeof(uint8_t) +
859 			(num_logical_children * sizeof(pldm_entity));
860 		rc = entity_association_pdr_add_children(
861 			curr, repo, logical_pdr_size, num_logical_children,
862 			PLDM_ENTITY_ASSOCIAION_LOGICAL, is_remote,
863 			terminus_handle, record_handle);
864 		if (rc < 0) {
865 			return rc;
866 		}
867 	}
868 
869 	if (num_physical_children) {
870 		uint16_t physical_pdr_size =
871 			sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) +
872 			sizeof(uint8_t) + sizeof(pldm_entity) +
873 			sizeof(uint8_t) +
874 			(num_physical_children * sizeof(pldm_entity));
875 		rc = entity_association_pdr_add_children(
876 			curr, repo, physical_pdr_size, num_physical_children,
877 			PLDM_ENTITY_ASSOCIAION_PHYSICAL, is_remote,
878 			terminus_handle, record_handle);
879 		if (rc < 0) {
880 			return rc;
881 		}
882 	}
883 
884 	return 0;
885 }
886 
887 static bool is_present(pldm_entity entity, pldm_entity **entities,
888 		       size_t num_entities)
889 {
890 	if (entities == NULL || num_entities == 0) {
891 		return true;
892 	}
893 	size_t i = 0;
894 	while (i < num_entities) {
895 		if ((*entities + i)->entity_type == entity.entity_type) {
896 			return true;
897 		}
898 		i++;
899 	}
900 	return false;
901 }
902 
903 static int entity_association_pdr_add(pldm_entity_node *curr, pldm_pdr *repo,
904 				      pldm_entity **entities,
905 				      size_t num_entities, bool is_remote,
906 				      uint16_t terminus_handle,
907 				      uint32_t record_handle)
908 {
909 	int rc;
910 
911 	if (curr == NULL) {
912 		return 0;
913 	}
914 
915 	if (is_present(curr->entity, entities, num_entities)) {
916 		rc = entity_association_pdr_add_entry(
917 			curr, repo, is_remote, terminus_handle, record_handle);
918 		if (rc) {
919 			return rc;
920 		}
921 	}
922 
923 	rc = entity_association_pdr_add(curr->next_sibling, repo, entities,
924 					num_entities, is_remote,
925 					terminus_handle, record_handle);
926 	if (rc) {
927 		return rc;
928 	}
929 
930 	return entity_association_pdr_add(curr->first_child, repo, entities,
931 					  num_entities, is_remote,
932 					  terminus_handle, record_handle);
933 }
934 
935 LIBPLDM_ABI_STABLE
936 int pldm_entity_association_pdr_add_check(pldm_entity_association_tree *tree,
937 					  pldm_pdr *repo, bool is_remote,
938 					  uint16_t terminus_handle)
939 {
940 	if (!tree || !repo) {
941 		return 0;
942 	}
943 
944 	return entity_association_pdr_add(tree->root, repo, NULL, 0, is_remote,
945 					  terminus_handle, 0);
946 }
947 
948 LIBPLDM_ABI_STABLE
949 int pldm_entity_association_pdr_add_from_node_check(
950 	pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
951 	size_t num_entities, bool is_remote, uint16_t terminus_handle)
952 {
953 	return pldm_entity_association_pdr_add_from_node_with_record_handle(
954 		node, repo, entities, num_entities, is_remote, terminus_handle,
955 		0);
956 }
957 
958 LIBPLDM_ABI_STABLE
959 int pldm_entity_association_pdr_add_from_node_with_record_handle(
960 	pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities,
961 	size_t num_entities, bool is_remote, uint16_t terminus_handle,
962 	uint32_t record_handle)
963 {
964 	if (!node || !repo || !entities) {
965 		return -EINVAL;
966 	}
967 
968 	entity_association_pdr_add(node, repo, entities, num_entities,
969 				   is_remote, terminus_handle, record_handle);
970 
971 	return 0;
972 }
973 
974 static void find_entity_ref_in_tree(pldm_entity_node *tree_node,
975 				    pldm_entity entity, pldm_entity_node **node)
976 {
977 	bool is_entity_container_id;
978 	bool is_entity_instance_num;
979 	bool is_type;
980 
981 	if (tree_node == NULL) {
982 		return;
983 	}
984 
985 	is_type = tree_node->entity.entity_type == entity.entity_type;
986 	is_entity_instance_num = tree_node->entity.entity_instance_num ==
987 				 entity.entity_instance_num;
988 	is_entity_container_id = tree_node->entity.entity_container_id ==
989 				 entity.entity_container_id;
990 
991 	if (is_type && is_entity_instance_num && is_entity_container_id) {
992 		*node = tree_node;
993 		return;
994 	}
995 
996 	find_entity_ref_in_tree(tree_node->first_child, entity, node);
997 	find_entity_ref_in_tree(tree_node->next_sibling, entity, node);
998 }
999 
1000 LIBPLDM_ABI_STABLE
1001 void pldm_find_entity_ref_in_tree(pldm_entity_association_tree *tree,
1002 				  pldm_entity entity, pldm_entity_node **node)
1003 {
1004 	if (!tree || !node) {
1005 		return;
1006 	}
1007 
1008 	find_entity_ref_in_tree(tree->root, entity, node);
1009 }
1010 
1011 LIBPLDM_ABI_STABLE
1012 void pldm_pdr_remove_pdrs_by_terminus_handle(pldm_pdr *repo,
1013 					     uint16_t terminus_handle)
1014 {
1015 	if (!repo) {
1016 		return;
1017 	}
1018 
1019 	bool removed = false;
1020 
1021 	pldm_pdr_record *record = repo->first;
1022 	pldm_pdr_record *prev = NULL;
1023 	while (record != NULL) {
1024 		pldm_pdr_record *next = record->next;
1025 		if (record->terminus_handle == terminus_handle) {
1026 			if (repo->first == record) {
1027 				repo->first = next;
1028 			} else {
1029 				prev->next = next;
1030 			}
1031 			if (repo->last == record) {
1032 				repo->last = prev;
1033 			}
1034 			if (record->data) {
1035 				free(record->data);
1036 			}
1037 			--repo->record_count;
1038 			repo->size -= record->size;
1039 			free(record);
1040 			removed = true;
1041 		} else {
1042 			prev = record;
1043 		}
1044 		record = next;
1045 	}
1046 
1047 	if (removed == true) {
1048 		record = repo->first;
1049 		uint32_t record_handle = 0;
1050 		while (record != NULL) {
1051 			record->record_handle = ++record_handle;
1052 			if (record->data != NULL) {
1053 				struct pldm_pdr_hdr *hdr =
1054 					(struct pldm_pdr_hdr *)(record->data);
1055 				hdr->record_handle =
1056 					htole32(record->record_handle);
1057 			}
1058 			record = record->next;
1059 		}
1060 	}
1061 }
1062 
1063 LIBPLDM_ABI_STABLE
1064 void pldm_pdr_remove_remote_pdrs(pldm_pdr *repo)
1065 {
1066 	if (!repo) {
1067 		return;
1068 	}
1069 
1070 	bool removed = false;
1071 
1072 	pldm_pdr_record *record = repo->first;
1073 	pldm_pdr_record *prev = NULL;
1074 	while (record != NULL) {
1075 		pldm_pdr_record *next = record->next;
1076 		if (record->is_remote == true) {
1077 			if (repo->first == record) {
1078 				repo->first = next;
1079 			} else {
1080 				prev->next = next;
1081 			}
1082 			if (repo->last == record) {
1083 				repo->last = prev;
1084 			}
1085 			if (record->data) {
1086 				free(record->data);
1087 			}
1088 			--repo->record_count;
1089 			repo->size -= record->size;
1090 			free(record);
1091 			removed = true;
1092 		} else {
1093 			prev = record;
1094 		}
1095 		record = next;
1096 	}
1097 
1098 	if (removed == true) {
1099 		record = repo->first;
1100 		uint32_t record_handle = 0;
1101 		while (record != NULL) {
1102 			record->record_handle = ++record_handle;
1103 			if (record->data != NULL) {
1104 				struct pldm_pdr_hdr *hdr =
1105 					(struct pldm_pdr_hdr *)(record->data);
1106 				hdr->record_handle =
1107 					htole32(record->record_handle);
1108 			}
1109 			record = record->next;
1110 		}
1111 	}
1112 }
1113 
1114 LIBPLDM_ABI_STABLE
1115 pldm_pdr_record *pldm_pdr_find_last_in_range(const pldm_pdr *repo,
1116 					     uint32_t first, uint32_t last)
1117 {
1118 	pldm_pdr_record *record = NULL;
1119 	pldm_pdr_record *curr;
1120 
1121 	if (!repo) {
1122 		return NULL;
1123 	}
1124 	for (curr = repo->first; curr; curr = curr->next) {
1125 		if (first > curr->record_handle || last < curr->record_handle) {
1126 			continue;
1127 		}
1128 		if (!record || curr->record_handle > record->record_handle) {
1129 			record = curr;
1130 		}
1131 	}
1132 
1133 	return record;
1134 }
1135 
1136 static void entity_association_tree_find_if_remote(pldm_entity_node *node,
1137 						   pldm_entity *entity,
1138 						   pldm_entity_node **out,
1139 						   bool is_remote)
1140 {
1141 	if (node == NULL) {
1142 		return;
1143 	}
1144 	bool is_entity_type;
1145 	bool is_entity_instance_num;
1146 
1147 	is_entity_type = node->entity.entity_type == entity->entity_type;
1148 	is_entity_instance_num = node->entity.entity_instance_num ==
1149 				 entity->entity_instance_num;
1150 
1151 	if (!is_remote ||
1152 	    node->remote_container_id == entity->entity_container_id) {
1153 		if (is_entity_type && is_entity_instance_num) {
1154 			entity->entity_container_id =
1155 				node->entity.entity_container_id;
1156 			*out = node;
1157 			return;
1158 		}
1159 	}
1160 	entity_association_tree_find_if_remote(node->next_sibling, entity, out,
1161 					       is_remote);
1162 	entity_association_tree_find_if_remote(node->first_child, entity, out,
1163 					       is_remote);
1164 }
1165 
1166 LIBPLDM_ABI_STABLE
1167 pldm_entity_node *pldm_entity_association_tree_find_with_locality(
1168 	pldm_entity_association_tree *tree, pldm_entity *entity, bool is_remote)
1169 {
1170 	if (!tree || !entity) {
1171 		return NULL;
1172 	}
1173 	pldm_entity_node *node = NULL;
1174 	entity_association_tree_find_if_remote(tree->root, entity, &node,
1175 					       is_remote);
1176 	return node;
1177 }
1178 
1179 static void entity_association_tree_find(pldm_entity_node *node,
1180 					 pldm_entity *entity,
1181 					 pldm_entity_node **out)
1182 {
1183 	if (node == NULL) {
1184 		return;
1185 	}
1186 
1187 	if (node->entity.entity_type == entity->entity_type &&
1188 	    node->entity.entity_instance_num == entity->entity_instance_num) {
1189 		entity->entity_container_id = node->entity.entity_container_id;
1190 		*out = node;
1191 		return;
1192 	}
1193 	entity_association_tree_find(node->next_sibling, entity, out);
1194 	entity_association_tree_find(node->first_child, entity, out);
1195 }
1196 
1197 LIBPLDM_ABI_STABLE
1198 pldm_entity_node *
1199 pldm_entity_association_tree_find(pldm_entity_association_tree *tree,
1200 				  pldm_entity *entity)
1201 {
1202 	if (!tree || !entity) {
1203 		return NULL;
1204 	}
1205 
1206 	pldm_entity_node *node = NULL;
1207 	entity_association_tree_find(tree->root, entity, &node);
1208 	return node;
1209 }
1210 
1211 static void entity_association_tree_copy(pldm_entity_node *org_node,
1212 					 pldm_entity_node **new_node)
1213 {
1214 	if (org_node == NULL) {
1215 		return;
1216 	}
1217 	*new_node = malloc(sizeof(pldm_entity_node));
1218 	(*new_node)->parent = org_node->parent;
1219 	(*new_node)->entity = org_node->entity;
1220 	(*new_node)->association_type = org_node->association_type;
1221 	(*new_node)->remote_container_id = org_node->remote_container_id;
1222 	(*new_node)->first_child = NULL;
1223 	(*new_node)->next_sibling = NULL;
1224 	entity_association_tree_copy(org_node->first_child,
1225 				     &((*new_node)->first_child));
1226 	entity_association_tree_copy(org_node->next_sibling,
1227 				     &((*new_node)->next_sibling));
1228 }
1229 
1230 LIBPLDM_ABI_STABLE
1231 void pldm_entity_association_tree_copy_root(
1232 	pldm_entity_association_tree *org_tree,
1233 	pldm_entity_association_tree *new_tree)
1234 {
1235 	assert(org_tree != NULL);
1236 	assert(new_tree != NULL);
1237 
1238 	new_tree->last_used_container_id = org_tree->last_used_container_id;
1239 	entity_association_tree_copy(org_tree->root, &(new_tree->root));
1240 }
1241 
1242 LIBPLDM_ABI_STABLE
1243 void pldm_entity_association_tree_destroy_root(
1244 	pldm_entity_association_tree *tree)
1245 {
1246 	if (!tree) {
1247 		return;
1248 	}
1249 
1250 	entity_association_tree_destroy(tree->root);
1251 	tree->last_used_container_id = 0;
1252 	tree->root = NULL;
1253 }
1254 
1255 LIBPLDM_ABI_STABLE
1256 bool pldm_is_empty_entity_assoc_tree(pldm_entity_association_tree *tree)
1257 {
1258 	return ((tree->root == NULL) ? true : false);
1259 }
1260 
1261 LIBPLDM_ABI_STABLE
1262 void pldm_entity_association_pdr_extract(const uint8_t *pdr, uint16_t pdr_len,
1263 					 size_t *num_entities,
1264 					 pldm_entity **entities)
1265 {
1266 	if (!pdr || !num_entities || !entities) {
1267 		return;
1268 	}
1269 	if (pdr_len < PDR_ENTITY_ASSOCIATION_MIN_SIZE) {
1270 		return;
1271 	}
1272 
1273 	struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)pdr;
1274 	if (hdr->type != PLDM_PDR_ENTITY_ASSOCIATION) {
1275 		return;
1276 	}
1277 
1278 	const uint8_t *start = (uint8_t *)pdr;
1279 	const uint8_t *end __attribute__((unused)) =
1280 		start + sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length);
1281 	start += sizeof(struct pldm_pdr_hdr);
1282 	struct pldm_pdr_entity_association *entity_association_pdr =
1283 		(struct pldm_pdr_entity_association *)start;
1284 	size_t l_num_entities = entity_association_pdr->num_children + 1;
1285 	if (l_num_entities < 2) {
1286 		return;
1287 	}
1288 	if (start + sizeof(struct pldm_pdr_entity_association) +
1289 		    sizeof(pldm_entity) * (l_num_entities - 2) !=
1290 	    end) {
1291 		return;
1292 	}
1293 	pldm_entity *l_entities = malloc(sizeof(pldm_entity) * l_num_entities);
1294 	if (!l_entities) {
1295 		return;
1296 	}
1297 	l_entities[0].entity_type =
1298 		le16toh(entity_association_pdr->container.entity_type);
1299 	l_entities[0].entity_instance_num =
1300 		le16toh(entity_association_pdr->container.entity_instance_num);
1301 	l_entities[0].entity_container_id =
1302 		le16toh(entity_association_pdr->container.entity_container_id);
1303 	pldm_entity *curr_entity = entity_association_pdr->children;
1304 	for (size_t i = 1; i < l_num_entities; i++, curr_entity++) {
1305 		l_entities[i].entity_type = le16toh(curr_entity->entity_type);
1306 		l_entities[i].entity_instance_num =
1307 			le16toh(curr_entity->entity_instance_num);
1308 		l_entities[i].entity_container_id =
1309 			le16toh(curr_entity->entity_container_id);
1310 	}
1311 
1312 	*num_entities = l_num_entities;
1313 	*entities = l_entities;
1314 }
1315 
1316 /* Find the position of record in pldm_pdr repo and place new_record in
1317  * the same position.
1318  */
1319 static int pldm_pdr_replace_record(pldm_pdr *repo, pldm_pdr_record *record,
1320 				   pldm_pdr_record *prev,
1321 				   pldm_pdr_record *new_record)
1322 {
1323 	assert(repo);
1324 	assert(record);
1325 	assert(prev);
1326 	assert(new_record);
1327 
1328 	if (repo->size < record->size) {
1329 		return -EOVERFLOW;
1330 	}
1331 
1332 	if (repo->size + new_record->size < new_record->size) {
1333 		return -EOVERFLOW;
1334 	}
1335 
1336 	if (repo->first == record) {
1337 		repo->first = new_record;
1338 	} else {
1339 		prev->next = new_record;
1340 	}
1341 	new_record->next = record->next;
1342 
1343 	if (repo->last == record) {
1344 		repo->last = new_record;
1345 	}
1346 
1347 	repo->size = (repo->size - record->size) + new_record->size;
1348 	return 0;
1349 }
1350 
1351 /* Insert a new record to pldm_pdr repo to a position that comes after
1352  * pldm_pdr_record record.
1353  */
1354 static int pldm_pdr_insert_record(pldm_pdr *repo, pldm_pdr_record *record,
1355 				  pldm_pdr_record *new_record)
1356 {
1357 	assert(repo);
1358 	assert(record);
1359 	assert(new_record);
1360 
1361 	if (repo->size + new_record->size < new_record->size) {
1362 		return -EOVERFLOW;
1363 	}
1364 
1365 	if (repo->record_count == UINT32_MAX) {
1366 		return -EOVERFLOW;
1367 	}
1368 
1369 	new_record->next = record->next;
1370 	record->next = new_record;
1371 
1372 	if (repo->last == record) {
1373 		repo->last = new_record;
1374 	}
1375 
1376 	repo->size = repo->size + new_record->size;
1377 	++repo->record_count;
1378 	return 0;
1379 }
1380 
1381 /* Find the position of PDR when its record handle is known
1382  */
1383 static bool pldm_pdr_find_record_by_handle(pldm_pdr_record **record,
1384 					   pldm_pdr_record **prev,
1385 					   uint32_t record_handle)
1386 {
1387 	assert(record);
1388 	assert(prev);
1389 
1390 	while (*record != NULL) {
1391 		if ((*record)->record_handle == record_handle) {
1392 			return true;
1393 		}
1394 		*prev = *record;
1395 		*record = (*record)->next;
1396 	}
1397 	return false;
1398 }
1399 
1400 LIBPLDM_ABI_TESTING
1401 int pldm_entity_association_pdr_add_contained_entity_to_remote_pdr(
1402 	pldm_pdr *repo, pldm_entity *entity, uint32_t pdr_record_handle)
1403 {
1404 	if (!repo || !entity) {
1405 		return -EINVAL;
1406 	}
1407 
1408 	pldm_pdr_record *record = repo->first;
1409 	pldm_pdr_record *prev = repo->first;
1410 	int rc = 0;
1411 	uint16_t header_length = 0;
1412 	uint8_t num_children = 0;
1413 	struct pldm_msgbuf _src;
1414 	struct pldm_msgbuf *src = &_src;
1415 	struct pldm_msgbuf _dst;
1416 	struct pldm_msgbuf *dst = &_dst;
1417 
1418 	pldm_pdr_find_record_by_handle(&record, &prev, pdr_record_handle);
1419 
1420 	if (!record) {
1421 		return -EINVAL;
1422 	}
1423 	// Initialize msg buffer for record and record->data
1424 	rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1425 				    record->data, record->size);
1426 	if (rc) {
1427 		return rc;
1428 	}
1429 
1430 	// check if adding another entity to record causes overflow before
1431 	// allocating memory for new_record.
1432 	if (record->size + sizeof(pldm_entity) < sizeof(pldm_entity)) {
1433 		return -EOVERFLOW;
1434 	}
1435 	pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1436 	if (!new_record) {
1437 		return -ENOMEM;
1438 	}
1439 
1440 	new_record->data = malloc(record->size + sizeof(pldm_entity));
1441 	if (!new_record->data) {
1442 		rc = -ENOMEM;
1443 		goto cleanup_new_record;
1444 	}
1445 
1446 	new_record->record_handle = record->record_handle;
1447 	new_record->size = record->size + sizeof(struct pldm_entity);
1448 	new_record->is_remote = record->is_remote;
1449 
1450 	// Initialize new PDR record with data from original PDR record.
1451 	// Start with adding the header of original PDR
1452 	rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1453 				    new_record->data, new_record->size);
1454 	if (rc) {
1455 		goto cleanup_new_record_data;
1456 	}
1457 
1458 	pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle);
1459 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_version);
1460 	pldm_msgbuf_copy(dst, src, uint8_t, hdr_type);
1461 	pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num);
1462 	// extract the header length from record and increment size with
1463 	// size of pldm_entity before inserting the value into new_record.
1464 	rc = pldm_msgbuf_extract(src, header_length);
1465 	if (rc) {
1466 		goto cleanup_new_record_data;
1467 	}
1468 	static_assert(UINT16_MAX < (SIZE_MAX - sizeof(pldm_entity)),
1469 		      "Fix the following bounds check.");
1470 	if (header_length + sizeof(pldm_entity) > UINT16_MAX) {
1471 		rc = -EOVERFLOW;
1472 		goto cleanup_new_record_data;
1473 	}
1474 	header_length += sizeof(pldm_entity);
1475 	pldm_msgbuf_insert(dst, header_length);
1476 	pldm_msgbuf_copy(dst, src, uint16_t, container_id);
1477 	pldm_msgbuf_copy(dst, src, uint8_t, association_type);
1478 	pldm_msgbuf_copy(dst, src, uint16_t, entity_type);
1479 	pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num);
1480 	pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id);
1481 	// extract value of number of children from record and increment it
1482 	// by 1 before insert the value to new record.
1483 	rc = pldm_msgbuf_extract(src, num_children);
1484 	if (rc) {
1485 		goto cleanup_new_record_data;
1486 	}
1487 	if (num_children == UINT8_MAX) {
1488 		rc = -EOVERFLOW;
1489 		goto cleanup_new_record_data;
1490 	}
1491 	num_children += 1;
1492 	pldm_msgbuf_insert(dst, num_children);
1493 	//Add all children of original PDR to new PDR
1494 	for (int i = 0; i < num_children - 1; i++) {
1495 		pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1496 		pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1497 		pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1498 	}
1499 
1500 	// Add new contained entity as a child of new PDR
1501 	rc = pldm_msgbuf_destroy(src);
1502 	if (rc) {
1503 		goto cleanup_new_record_data;
1504 	}
1505 	rc = pldm_msgbuf_init_errno(src, sizeof(struct pldm_entity), entity,
1506 				    sizeof(struct pldm_entity));
1507 	if (rc) {
1508 		goto cleanup_new_record_data;
1509 	}
1510 	pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type);
1511 	pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num);
1512 	pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id);
1513 
1514 	rc = pldm_msgbuf_destroy(src);
1515 	if (rc) {
1516 		goto cleanup_new_record_data;
1517 	}
1518 	rc = pldm_msgbuf_destroy(dst);
1519 	if (rc) {
1520 		goto cleanup_new_record_data;
1521 	}
1522 
1523 	rc = pldm_pdr_replace_record(repo, record, prev, new_record);
1524 	if (rc) {
1525 		goto cleanup_new_record_data;
1526 	}
1527 
1528 	free(record->data);
1529 	free(record);
1530 	return rc;
1531 cleanup_new_record_data:
1532 	free(new_record->data);
1533 cleanup_new_record:
1534 	free(new_record);
1535 	return rc;
1536 }
1537 
1538 LIBPLDM_ABI_TESTING
1539 int pldm_entity_association_pdr_create_new(pldm_pdr *repo,
1540 					   uint32_t pdr_record_handle,
1541 					   pldm_entity *parent,
1542 					   pldm_entity *entity,
1543 					   uint32_t *entity_record_handle)
1544 {
1545 	if (!repo || !parent || !entity || !entity_record_handle) {
1546 		return -EINVAL;
1547 	}
1548 
1549 	if (pdr_record_handle == UINT32_MAX) {
1550 		return -EOVERFLOW;
1551 	}
1552 
1553 	bool pdr_added = false;
1554 	uint16_t new_pdr_size;
1555 	uint16_t container_id = 0;
1556 	uint8_t *container_id_addr = NULL;
1557 	struct pldm_msgbuf _dst;
1558 	struct pldm_msgbuf *dst = &_dst;
1559 	struct pldm_msgbuf _src_p;
1560 	struct pldm_msgbuf *src_p = &_src_p;
1561 	struct pldm_msgbuf _src_c;
1562 	struct pldm_msgbuf *src_c = &_src_c;
1563 	int rc = 0;
1564 
1565 	pldm_pdr_record *prev = repo->first;
1566 	pldm_pdr_record *record = repo->first;
1567 	pdr_added = pldm_pdr_find_record_by_handle(&record, &prev,
1568 						   pdr_record_handle);
1569 	if (!pdr_added) {
1570 		return -ENOENT;
1571 	}
1572 
1573 	static_assert(PDR_ENTITY_ASSOCIATION_MIN_SIZE < UINT16_MAX,
1574 		      "Truncation ahead");
1575 	new_pdr_size = PDR_ENTITY_ASSOCIATION_MIN_SIZE;
1576 	pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record));
1577 	if (!new_record) {
1578 		return -ENOMEM;
1579 	}
1580 
1581 	new_record->data = malloc(new_pdr_size);
1582 	if (!new_record->data) {
1583 		rc = -ENOMEM;
1584 		goto cleanup_new_record;
1585 	}
1586 
1587 	// Initialise new PDR to be added with the header, size and handle.
1588 	// Set the position of new PDR
1589 	*entity_record_handle = pdr_record_handle + 1;
1590 	new_record->record_handle = *entity_record_handle;
1591 	new_record->size = new_pdr_size;
1592 	new_record->is_remote = false;
1593 
1594 	rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE,
1595 				    new_record->data, new_record->size);
1596 	if (rc) {
1597 		goto cleanup_new_record_data;
1598 	}
1599 
1600 	// header record handle
1601 	pldm_msgbuf_insert(dst, *entity_record_handle);
1602 	// header version
1603 	pldm_msgbuf_insert_uint8(dst, 1);
1604 	// header type
1605 	pldm_msgbuf_insert_uint8(dst, PLDM_PDR_ENTITY_ASSOCIATION);
1606 	// header change number
1607 	pldm_msgbuf_insert_uint16(dst, 0);
1608 	// header length
1609 	pldm_msgbuf_insert_uint16(dst,
1610 				  (new_pdr_size - sizeof(struct pldm_pdr_hdr)));
1611 
1612 	// Data for new PDR is obtained from parent PDR and new contained entity
1613 	// is added as the child
1614 	rc = pldm_msgbuf_init_errno(src_p, sizeof(struct pldm_entity), parent,
1615 				    sizeof(*parent));
1616 	if (rc) {
1617 		goto cleanup_new_record_data;
1618 	}
1619 
1620 	rc = pldm_msgbuf_init_errno(src_c, sizeof(struct pldm_entity), entity,
1621 				    sizeof(*entity));
1622 	if (rc) {
1623 		goto cleanup_new_record_data;
1624 	}
1625 
1626 	// extract pointer for container ID and save the address
1627 	rc = pldm_msgbuf_span_required(dst, sizeof(container_id),
1628 				       (void **)&container_id_addr);
1629 	if (rc) {
1630 		goto cleanup_new_record_data;
1631 	}
1632 	pldm_msgbuf_insert_uint8(dst, PLDM_ENTITY_ASSOCIAION_PHYSICAL);
1633 	pldm_msgbuf_copy(dst, src_p, uint16_t, entity_type);
1634 	pldm_msgbuf_copy(dst, src_p, uint16_t, entity_instance_num);
1635 	pldm_msgbuf_copy(dst, src_p, uint16_t, entity_container_id);
1636 	// number of children
1637 	pldm_msgbuf_insert_uint8(dst, 1);
1638 
1639 	// Add new entity as child
1640 	pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_type);
1641 	pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_instance_num);
1642 	// Extract and insert child entity container ID and add same value to
1643 	// container ID of entity
1644 	pldm_msgbuf_extract(src_c, container_id);
1645 	pldm_msgbuf_insert(dst, container_id);
1646 	container_id = htole16(container_id);
1647 	memcpy(container_id_addr, &container_id, sizeof(uint16_t));
1648 
1649 	rc = pldm_msgbuf_destroy(dst);
1650 	if (rc) {
1651 		goto cleanup_new_record_data;
1652 	}
1653 	rc = pldm_msgbuf_destroy(src_p);
1654 	if (rc) {
1655 		goto cleanup_new_record_data;
1656 	}
1657 	rc = pldm_msgbuf_destroy(src_c);
1658 	if (rc) {
1659 		goto cleanup_new_record_data;
1660 	}
1661 
1662 	rc = pldm_pdr_insert_record(repo, record, new_record);
1663 	if (rc) {
1664 		goto cleanup_new_record_data;
1665 	}
1666 
1667 	return rc;
1668 cleanup_new_record_data:
1669 	free(new_record->data);
1670 cleanup_new_record:
1671 	free(new_record);
1672 	return rc;
1673 }
1674