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