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