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