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