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