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