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