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