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