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