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_CC_NONNULL 465 static inline uint16_t next_container_id(pldm_entity_association_tree *tree) 466 { 467 assert(tree->last_used_container_id != UINT16_MAX); 468 469 return ++tree->last_used_container_id; 470 } 471 472 LIBPLDM_ABI_STABLE 473 pldm_entity pldm_entity_extract(pldm_entity_node *node) 474 { 475 assert(node != NULL); 476 477 return node->entity; 478 } 479 480 LIBPLDM_ABI_STABLE 481 uint16_t 482 pldm_entity_node_get_remote_container_id(const pldm_entity_node *entity) 483 { 484 assert(entity != NULL); 485 486 return entity->remote_container_id; 487 } 488 489 LIBPLDM_ABI_STABLE 490 pldm_entity_association_tree *pldm_entity_association_tree_init(void) 491 { 492 pldm_entity_association_tree *tree = 493 malloc(sizeof(pldm_entity_association_tree)); 494 if (!tree) { 495 return NULL; 496 } 497 tree->root = NULL; 498 tree->last_used_container_id = 0; 499 500 return tree; 501 } 502 503 LIBPLDM_CC_NONNULL 504 static pldm_entity_node *find_insertion_at(pldm_entity_node *start, 505 uint16_t entity_type) 506 { 507 /* Insert after the the last node that matches the input entity type, or 508 * at the end if no such match occurs 509 */ 510 while (start->next_sibling != NULL) { 511 uint16_t this_type = start->entity.entity_type; 512 pldm_entity_node *next = start->next_sibling; 513 if (this_type == entity_type && 514 (this_type != next->entity.entity_type)) { 515 break; 516 } 517 start = start->next_sibling; 518 } 519 520 return start; 521 } 522 523 LIBPLDM_ABI_STABLE 524 pldm_entity_node *pldm_entity_association_tree_add( 525 pldm_entity_association_tree *tree, pldm_entity *entity, 526 uint16_t entity_instance_number, pldm_entity_node *parent, 527 uint8_t association_type) 528 { 529 return pldm_entity_association_tree_add_entity(tree, entity, 530 entity_instance_number, 531 parent, association_type, 532 false, true, 0xffff); 533 } 534 535 LIBPLDM_ABI_STABLE 536 pldm_entity_node *pldm_entity_association_tree_add_entity( 537 pldm_entity_association_tree *tree, pldm_entity *entity, 538 uint16_t entity_instance_number, pldm_entity_node *parent, 539 uint8_t association_type, bool is_remote, bool is_update_container_id, 540 uint16_t container_id) 541 { 542 if ((!tree) || (!entity)) { 543 return NULL; 544 } 545 546 if (entity_instance_number != 0xffff && parent != NULL) { 547 pldm_entity node; 548 node.entity_type = entity->entity_type; 549 node.entity_instance_num = entity_instance_number; 550 if (pldm_is_current_parent_child(parent, &node)) { 551 return NULL; 552 } 553 } 554 if (association_type != PLDM_ENTITY_ASSOCIAION_PHYSICAL && 555 association_type != PLDM_ENTITY_ASSOCIAION_LOGICAL) { 556 return NULL; 557 } 558 pldm_entity_node *node = malloc(sizeof(pldm_entity_node)); 559 if (!node) { 560 return NULL; 561 } 562 node->first_child = NULL; 563 node->next_sibling = NULL; 564 node->parent.entity_type = 0; 565 node->parent.entity_instance_num = 0; 566 node->parent.entity_container_id = 0; 567 node->entity.entity_type = entity->entity_type; 568 node->entity.entity_instance_num = 569 entity_instance_number != 0xffff ? entity_instance_number : 1; 570 node->association_type = association_type; 571 node->remote_container_id = 0; 572 if (tree->root == NULL) { 573 if (parent != NULL) { 574 free(node); 575 return NULL; 576 } 577 tree->root = node; 578 /* container_id 0 here indicates this is the top-most entry */ 579 node->entity.entity_container_id = 0; 580 node->remote_container_id = node->entity.entity_container_id; 581 } else if (parent != NULL && parent->first_child == NULL) { 582 /* Ensure next_container_id() will yield a valid ID */ 583 if (tree->last_used_container_id == UINT16_MAX) { 584 free(node); 585 return NULL; 586 } 587 588 parent->first_child = node; 589 node->parent = parent->entity; 590 591 if (is_remote) { 592 node->remote_container_id = entity->entity_container_id; 593 } 594 if (is_update_container_id) { 595 if (container_id != 0xffff) { 596 node->entity.entity_container_id = container_id; 597 } else { 598 node->entity.entity_container_id = 599 next_container_id(tree); 600 } 601 } else { 602 node->entity.entity_container_id = 603 entity->entity_container_id; 604 } 605 606 if (!is_remote) { 607 node->remote_container_id = 608 node->entity.entity_container_id; 609 } 610 } else { 611 pldm_entity_node *start = parent == NULL ? tree->root : 612 parent->first_child; 613 pldm_entity_node *prev = 614 find_insertion_at(start, entity->entity_type); 615 if (!prev) { 616 free(node); 617 return NULL; 618 } 619 pldm_entity_node *next = prev->next_sibling; 620 if (prev->entity.entity_type == entity->entity_type) { 621 if (prev->entity.entity_instance_num == UINT16_MAX) { 622 free(node); 623 return NULL; 624 } 625 node->entity.entity_instance_num = 626 entity_instance_number != 0xffff ? 627 entity_instance_number : 628 prev->entity.entity_instance_num + 1; 629 } 630 prev->next_sibling = node; 631 node->parent = prev->parent; 632 node->next_sibling = next; 633 node->entity.entity_container_id = 634 prev->entity.entity_container_id; 635 node->remote_container_id = entity->entity_container_id; 636 } 637 entity->entity_instance_num = node->entity.entity_instance_num; 638 if (is_update_container_id) { 639 entity->entity_container_id = node->entity.entity_container_id; 640 } 641 return node; 642 } 643 644 static void get_num_nodes(pldm_entity_node *node, size_t *num) 645 { 646 if (node == NULL) { 647 return; 648 } 649 650 ++(*num); 651 get_num_nodes(node->next_sibling, num); 652 get_num_nodes(node->first_child, num); 653 } 654 655 static void entity_association_tree_visit(pldm_entity_node *node, 656 pldm_entity *entities, size_t *index) 657 { 658 if (node == NULL) { 659 return; 660 } 661 662 pldm_entity *entity = &entities[*index]; 663 ++(*index); 664 entity->entity_type = node->entity.entity_type; 665 entity->entity_instance_num = node->entity.entity_instance_num; 666 entity->entity_container_id = node->entity.entity_container_id; 667 668 entity_association_tree_visit(node->next_sibling, entities, index); 669 entity_association_tree_visit(node->first_child, entities, index); 670 } 671 672 LIBPLDM_ABI_STABLE 673 void pldm_entity_association_tree_visit(pldm_entity_association_tree *tree, 674 pldm_entity **entities, size_t *size) 675 { 676 if (!tree || !entities || !size) { 677 return; 678 } 679 680 *size = 0; 681 if (tree->root == NULL) { 682 return; 683 } 684 685 get_num_nodes(tree->root, size); 686 *entities = malloc(*size * sizeof(pldm_entity)); 687 if (!entities) { 688 return; 689 } 690 size_t index = 0; 691 entity_association_tree_visit(tree->root, *entities, &index); 692 } 693 694 static void entity_association_tree_destroy(pldm_entity_node *node) 695 { 696 if (node == NULL) { 697 return; 698 } 699 700 entity_association_tree_destroy(node->next_sibling); 701 entity_association_tree_destroy(node->first_child); 702 free(node); 703 } 704 705 LIBPLDM_ABI_STABLE 706 void pldm_entity_association_tree_destroy(pldm_entity_association_tree *tree) 707 { 708 if (!tree) { 709 return; 710 } 711 712 entity_association_tree_destroy(tree->root); 713 free(tree); 714 } 715 716 LIBPLDM_ABI_STABLE 717 bool pldm_entity_is_node_parent(pldm_entity_node *node) 718 { 719 assert(node != NULL); 720 721 return node->first_child != NULL; 722 } 723 724 LIBPLDM_ABI_STABLE 725 pldm_entity pldm_entity_get_parent(pldm_entity_node *node) 726 { 727 assert(node != NULL); 728 729 return node->parent; 730 } 731 732 LIBPLDM_ABI_STABLE 733 bool pldm_entity_is_exist_parent(pldm_entity_node *node) 734 { 735 if (!node) { 736 return false; 737 } 738 739 if (node->parent.entity_type == 0 && 740 node->parent.entity_instance_num == 0 && 741 node->parent.entity_container_id == 0) { 742 return false; 743 } 744 745 return true; 746 } 747 748 LIBPLDM_ABI_STABLE 749 uint8_t pldm_entity_get_num_children(pldm_entity_node *node, 750 uint8_t association_type) 751 { 752 if (!node) { 753 return 0; 754 } 755 756 if (!(association_type == PLDM_ENTITY_ASSOCIAION_PHYSICAL || 757 association_type == PLDM_ENTITY_ASSOCIAION_LOGICAL)) { 758 return 0; 759 } 760 761 size_t count = 0; 762 pldm_entity_node *curr = node->first_child; 763 while (curr != NULL) { 764 if (curr->association_type == association_type) { 765 ++count; 766 } 767 curr = curr->next_sibling; 768 } 769 770 assert(count < UINT8_MAX); 771 return count < UINT8_MAX ? count : 0; 772 } 773 774 LIBPLDM_ABI_STABLE 775 bool pldm_is_current_parent_child(pldm_entity_node *parent, pldm_entity *node) 776 { 777 if (!parent || !node) { 778 return false; 779 } 780 781 pldm_entity_node *curr = parent->first_child; 782 while (curr != NULL) { 783 if (node->entity_type == curr->entity.entity_type && 784 node->entity_instance_num == 785 curr->entity.entity_instance_num) { 786 return true; 787 } 788 curr = curr->next_sibling; 789 } 790 791 return false; 792 } 793 794 static int entity_association_pdr_add_children( 795 pldm_entity_node *curr, pldm_pdr *repo, uint16_t size, 796 uint8_t contained_count, uint8_t association_type, bool is_remote, 797 uint16_t terminus_handle, uint32_t record_handle) 798 { 799 uint8_t *start; 800 uint8_t *pdr; 801 int rc; 802 803 pdr = calloc(1, size); 804 if (!pdr) { 805 return -ENOMEM; 806 } 807 808 start = pdr; 809 810 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)start; 811 hdr->version = 1; 812 hdr->record_handle = record_handle; 813 hdr->type = PLDM_PDR_ENTITY_ASSOCIATION; 814 hdr->record_change_num = 0; 815 hdr->length = htole16(size - sizeof(struct pldm_pdr_hdr)); 816 start += sizeof(struct pldm_pdr_hdr); 817 818 uint16_t *container_id = (uint16_t *)start; 819 *container_id = htole16(curr->first_child->entity.entity_container_id); 820 start += sizeof(uint16_t); 821 *start = association_type; 822 start += sizeof(uint8_t); 823 824 pldm_entity *entity = (pldm_entity *)start; 825 entity->entity_type = htole16(curr->entity.entity_type); 826 entity->entity_instance_num = htole16(curr->entity.entity_instance_num); 827 entity->entity_container_id = htole16(curr->entity.entity_container_id); 828 start += sizeof(pldm_entity); 829 830 *start = contained_count; 831 start += sizeof(uint8_t); 832 833 pldm_entity_node *node = curr->first_child; 834 while (node != NULL) { 835 if (node->association_type == association_type) { 836 pldm_entity *entity = (pldm_entity *)start; 837 entity->entity_type = htole16(node->entity.entity_type); 838 entity->entity_instance_num = 839 htole16(node->entity.entity_instance_num); 840 entity->entity_container_id = 841 htole16(node->entity.entity_container_id); 842 start += sizeof(pldm_entity); 843 } 844 node = node->next_sibling; 845 } 846 847 rc = pldm_pdr_add(repo, pdr, size, is_remote, terminus_handle, 848 &record_handle); 849 free(pdr); 850 return rc; 851 } 852 853 static int entity_association_pdr_add_entry(pldm_entity_node *curr, 854 pldm_pdr *repo, bool is_remote, 855 uint16_t terminus_handle, 856 uint32_t record_handle) 857 { 858 uint8_t num_logical_children = pldm_entity_get_num_children( 859 curr, PLDM_ENTITY_ASSOCIAION_LOGICAL); 860 uint8_t num_physical_children = pldm_entity_get_num_children( 861 curr, PLDM_ENTITY_ASSOCIAION_PHYSICAL); 862 int rc; 863 864 if (num_logical_children) { 865 uint16_t logical_pdr_size = 866 sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) + 867 sizeof(uint8_t) + sizeof(pldm_entity) + 868 sizeof(uint8_t) + 869 (num_logical_children * sizeof(pldm_entity)); 870 rc = entity_association_pdr_add_children( 871 curr, repo, logical_pdr_size, num_logical_children, 872 PLDM_ENTITY_ASSOCIAION_LOGICAL, is_remote, 873 terminus_handle, record_handle); 874 if (rc < 0) { 875 return rc; 876 } 877 } 878 879 if (num_physical_children) { 880 uint16_t physical_pdr_size = 881 sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t) + 882 sizeof(uint8_t) + sizeof(pldm_entity) + 883 sizeof(uint8_t) + 884 (num_physical_children * sizeof(pldm_entity)); 885 rc = entity_association_pdr_add_children( 886 curr, repo, physical_pdr_size, num_physical_children, 887 PLDM_ENTITY_ASSOCIAION_PHYSICAL, is_remote, 888 terminus_handle, record_handle); 889 if (rc < 0) { 890 return rc; 891 } 892 } 893 894 return 0; 895 } 896 897 static bool is_present(pldm_entity entity, pldm_entity **entities, 898 size_t num_entities) 899 { 900 if (entities == NULL || num_entities == 0) { 901 return true; 902 } 903 size_t i = 0; 904 while (i < num_entities) { 905 if ((*entities + i)->entity_type == entity.entity_type) { 906 return true; 907 } 908 i++; 909 } 910 return false; 911 } 912 913 static int entity_association_pdr_add(pldm_entity_node *curr, pldm_pdr *repo, 914 pldm_entity **entities, 915 size_t num_entities, bool is_remote, 916 uint16_t terminus_handle, 917 uint32_t record_handle) 918 { 919 int rc; 920 921 if (curr == NULL) { 922 return 0; 923 } 924 925 if (is_present(curr->entity, entities, num_entities)) { 926 rc = entity_association_pdr_add_entry( 927 curr, repo, is_remote, terminus_handle, record_handle); 928 if (rc) { 929 return rc; 930 } 931 } 932 933 rc = entity_association_pdr_add(curr->next_sibling, repo, entities, 934 num_entities, is_remote, 935 terminus_handle, record_handle); 936 if (rc) { 937 return rc; 938 } 939 940 return entity_association_pdr_add(curr->first_child, repo, entities, 941 num_entities, is_remote, 942 terminus_handle, record_handle); 943 } 944 945 LIBPLDM_ABI_STABLE 946 int pldm_entity_association_pdr_add(pldm_entity_association_tree *tree, 947 pldm_pdr *repo, bool is_remote, 948 uint16_t terminus_handle) 949 { 950 if (!tree || !repo) { 951 return 0; 952 } 953 954 return entity_association_pdr_add(tree->root, repo, NULL, 0, is_remote, 955 terminus_handle, 0); 956 } 957 958 LIBPLDM_ABI_STABLE 959 int pldm_entity_association_pdr_add_from_node( 960 pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities, 961 size_t num_entities, bool is_remote, uint16_t terminus_handle) 962 { 963 return pldm_entity_association_pdr_add_from_node_with_record_handle( 964 node, repo, entities, num_entities, is_remote, terminus_handle, 965 0); 966 } 967 968 LIBPLDM_ABI_STABLE 969 int pldm_entity_association_pdr_add_from_node_with_record_handle( 970 pldm_entity_node *node, pldm_pdr *repo, pldm_entity **entities, 971 size_t num_entities, bool is_remote, uint16_t terminus_handle, 972 uint32_t record_handle) 973 { 974 if (!node || !repo || !entities) { 975 return -EINVAL; 976 } 977 978 entity_association_pdr_add(node, repo, entities, num_entities, 979 is_remote, terminus_handle, record_handle); 980 981 return 0; 982 } 983 984 static void find_entity_ref_in_tree(pldm_entity_node *tree_node, 985 pldm_entity entity, pldm_entity_node **node) 986 { 987 bool is_entity_container_id; 988 bool is_entity_instance_num; 989 bool is_type; 990 991 if (tree_node == NULL) { 992 return; 993 } 994 995 is_type = tree_node->entity.entity_type == entity.entity_type; 996 is_entity_instance_num = tree_node->entity.entity_instance_num == 997 entity.entity_instance_num; 998 is_entity_container_id = tree_node->entity.entity_container_id == 999 entity.entity_container_id; 1000 1001 if (is_type && is_entity_instance_num && is_entity_container_id) { 1002 *node = tree_node; 1003 return; 1004 } 1005 1006 find_entity_ref_in_tree(tree_node->first_child, entity, node); 1007 find_entity_ref_in_tree(tree_node->next_sibling, entity, node); 1008 } 1009 1010 LIBPLDM_ABI_STABLE 1011 void pldm_find_entity_ref_in_tree(pldm_entity_association_tree *tree, 1012 pldm_entity entity, pldm_entity_node **node) 1013 { 1014 if (!tree || !node) { 1015 return; 1016 } 1017 1018 find_entity_ref_in_tree(tree->root, entity, node); 1019 } 1020 1021 LIBPLDM_ABI_STABLE 1022 void pldm_pdr_remove_pdrs_by_terminus_handle(pldm_pdr *repo, 1023 uint16_t terminus_handle) 1024 { 1025 if (!repo) { 1026 return; 1027 } 1028 1029 bool removed = false; 1030 1031 pldm_pdr_record *record = repo->first; 1032 pldm_pdr_record *prev = NULL; 1033 while (record != NULL) { 1034 pldm_pdr_record *next = record->next; 1035 if (record->terminus_handle == terminus_handle) { 1036 if (repo->first == record) { 1037 repo->first = next; 1038 } else { 1039 prev->next = next; 1040 } 1041 if (repo->last == record) { 1042 repo->last = prev; 1043 } 1044 if (record->data) { 1045 free(record->data); 1046 } 1047 --repo->record_count; 1048 repo->size -= record->size; 1049 free(record); 1050 removed = true; 1051 } else { 1052 prev = record; 1053 } 1054 record = next; 1055 } 1056 1057 if (removed == true) { 1058 record = repo->first; 1059 uint32_t record_handle = 0; 1060 while (record != NULL) { 1061 record->record_handle = ++record_handle; 1062 if (record->data != NULL) { 1063 struct pldm_pdr_hdr *hdr = 1064 (struct pldm_pdr_hdr *)(record->data); 1065 hdr->record_handle = 1066 htole32(record->record_handle); 1067 } 1068 record = record->next; 1069 } 1070 } 1071 } 1072 1073 LIBPLDM_ABI_STABLE 1074 void pldm_pdr_remove_remote_pdrs(pldm_pdr *repo) 1075 { 1076 if (!repo) { 1077 return; 1078 } 1079 1080 bool removed = false; 1081 1082 pldm_pdr_record *record = repo->first; 1083 pldm_pdr_record *prev = NULL; 1084 while (record != NULL) { 1085 pldm_pdr_record *next = record->next; 1086 if (record->is_remote == true) { 1087 if (repo->first == record) { 1088 repo->first = next; 1089 } else { 1090 prev->next = next; 1091 } 1092 if (repo->last == record) { 1093 repo->last = prev; 1094 } 1095 if (record->data) { 1096 free(record->data); 1097 } 1098 --repo->record_count; 1099 repo->size -= record->size; 1100 free(record); 1101 removed = true; 1102 } else { 1103 prev = record; 1104 } 1105 record = next; 1106 } 1107 1108 if (removed == true) { 1109 record = repo->first; 1110 uint32_t record_handle = 0; 1111 while (record != NULL) { 1112 record->record_handle = ++record_handle; 1113 if (record->data != NULL) { 1114 struct pldm_pdr_hdr *hdr = 1115 (struct pldm_pdr_hdr *)(record->data); 1116 hdr->record_handle = 1117 htole32(record->record_handle); 1118 } 1119 record = record->next; 1120 } 1121 } 1122 } 1123 1124 LIBPLDM_ABI_STABLE 1125 pldm_pdr_record *pldm_pdr_find_last_in_range(const pldm_pdr *repo, 1126 uint32_t first, uint32_t last) 1127 { 1128 pldm_pdr_record *record = NULL; 1129 pldm_pdr_record *curr; 1130 1131 if (!repo) { 1132 return NULL; 1133 } 1134 for (curr = repo->first; curr; curr = curr->next) { 1135 if (first > curr->record_handle || last < curr->record_handle) { 1136 continue; 1137 } 1138 if (!record || curr->record_handle > record->record_handle) { 1139 record = curr; 1140 } 1141 } 1142 1143 return record; 1144 } 1145 1146 static void entity_association_tree_find_if_remote(pldm_entity_node *node, 1147 pldm_entity *entity, 1148 pldm_entity_node **out, 1149 bool is_remote) 1150 { 1151 if (node == NULL) { 1152 return; 1153 } 1154 bool is_entity_type; 1155 bool is_entity_instance_num; 1156 1157 is_entity_type = node->entity.entity_type == entity->entity_type; 1158 is_entity_instance_num = node->entity.entity_instance_num == 1159 entity->entity_instance_num; 1160 1161 if (!is_remote || 1162 node->remote_container_id == entity->entity_container_id) { 1163 if (is_entity_type && is_entity_instance_num) { 1164 entity->entity_container_id = 1165 node->entity.entity_container_id; 1166 *out = node; 1167 return; 1168 } 1169 } 1170 entity_association_tree_find_if_remote(node->next_sibling, entity, out, 1171 is_remote); 1172 entity_association_tree_find_if_remote(node->first_child, entity, out, 1173 is_remote); 1174 } 1175 1176 LIBPLDM_ABI_STABLE 1177 pldm_entity_node *pldm_entity_association_tree_find_with_locality( 1178 pldm_entity_association_tree *tree, pldm_entity *entity, bool is_remote) 1179 { 1180 if (!tree || !entity) { 1181 return NULL; 1182 } 1183 pldm_entity_node *node = NULL; 1184 entity_association_tree_find_if_remote(tree->root, entity, &node, 1185 is_remote); 1186 return node; 1187 } 1188 1189 static void entity_association_tree_find(pldm_entity_node *node, 1190 pldm_entity *entity, 1191 pldm_entity_node **out) 1192 { 1193 if (node == NULL) { 1194 return; 1195 } 1196 1197 if (node->entity.entity_type == entity->entity_type && 1198 node->entity.entity_instance_num == entity->entity_instance_num) { 1199 entity->entity_container_id = node->entity.entity_container_id; 1200 *out = node; 1201 return; 1202 } 1203 entity_association_tree_find(node->next_sibling, entity, out); 1204 entity_association_tree_find(node->first_child, entity, out); 1205 } 1206 1207 LIBPLDM_ABI_STABLE 1208 pldm_entity_node * 1209 pldm_entity_association_tree_find(pldm_entity_association_tree *tree, 1210 pldm_entity *entity) 1211 { 1212 if (!tree || !entity) { 1213 return NULL; 1214 } 1215 1216 pldm_entity_node *node = NULL; 1217 entity_association_tree_find(tree->root, entity, &node); 1218 return node; 1219 } 1220 1221 static void entity_association_tree_copy(pldm_entity_node *org_node, 1222 pldm_entity_node **new_node) 1223 { 1224 if (org_node == NULL) { 1225 return; 1226 } 1227 *new_node = malloc(sizeof(pldm_entity_node)); 1228 (*new_node)->parent = org_node->parent; 1229 (*new_node)->entity = org_node->entity; 1230 (*new_node)->association_type = org_node->association_type; 1231 (*new_node)->remote_container_id = org_node->remote_container_id; 1232 (*new_node)->first_child = NULL; 1233 (*new_node)->next_sibling = NULL; 1234 entity_association_tree_copy(org_node->first_child, 1235 &((*new_node)->first_child)); 1236 entity_association_tree_copy(org_node->next_sibling, 1237 &((*new_node)->next_sibling)); 1238 } 1239 1240 LIBPLDM_ABI_STABLE 1241 void pldm_entity_association_tree_copy_root( 1242 pldm_entity_association_tree *org_tree, 1243 pldm_entity_association_tree *new_tree) 1244 { 1245 assert(org_tree != NULL); 1246 assert(new_tree != NULL); 1247 1248 new_tree->last_used_container_id = org_tree->last_used_container_id; 1249 entity_association_tree_copy(org_tree->root, &(new_tree->root)); 1250 } 1251 1252 LIBPLDM_ABI_STABLE 1253 void pldm_entity_association_tree_destroy_root( 1254 pldm_entity_association_tree *tree) 1255 { 1256 if (!tree) { 1257 return; 1258 } 1259 1260 entity_association_tree_destroy(tree->root); 1261 tree->last_used_container_id = 0; 1262 tree->root = NULL; 1263 } 1264 1265 LIBPLDM_ABI_STABLE 1266 bool pldm_is_empty_entity_assoc_tree(pldm_entity_association_tree *tree) 1267 { 1268 return ((tree->root == NULL) ? true : false); 1269 } 1270 1271 LIBPLDM_ABI_STABLE 1272 void pldm_entity_association_pdr_extract(const uint8_t *pdr, uint16_t pdr_len, 1273 size_t *num_entities, 1274 pldm_entity **entities) 1275 { 1276 if (!pdr || !num_entities || !entities) { 1277 return; 1278 } 1279 if (pdr_len < PDR_ENTITY_ASSOCIATION_MIN_SIZE) { 1280 return; 1281 } 1282 1283 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)pdr; 1284 if (hdr->type != PLDM_PDR_ENTITY_ASSOCIATION) { 1285 return; 1286 } 1287 1288 const uint8_t *start = (uint8_t *)pdr; 1289 const uint8_t *end LIBPLDM_CC_UNUSED = 1290 start + sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length); 1291 start += sizeof(struct pldm_pdr_hdr); 1292 struct pldm_pdr_entity_association *entity_association_pdr = 1293 (struct pldm_pdr_entity_association *)start; 1294 size_t l_num_entities = entity_association_pdr->num_children + 1; 1295 if (l_num_entities < 2) { 1296 return; 1297 } 1298 if (start + sizeof(struct pldm_pdr_entity_association) + 1299 sizeof(pldm_entity) * (l_num_entities - 2) != 1300 end) { 1301 return; 1302 } 1303 pldm_entity *l_entities = malloc(sizeof(pldm_entity) * l_num_entities); 1304 if (!l_entities) { 1305 return; 1306 } 1307 l_entities[0].entity_type = 1308 le16toh(entity_association_pdr->container.entity_type); 1309 l_entities[0].entity_instance_num = 1310 le16toh(entity_association_pdr->container.entity_instance_num); 1311 l_entities[0].entity_container_id = 1312 le16toh(entity_association_pdr->container.entity_container_id); 1313 pldm_entity *curr_entity = entity_association_pdr->children; 1314 for (size_t i = 1; i < l_num_entities; i++, curr_entity++) { 1315 l_entities[i].entity_type = le16toh(curr_entity->entity_type); 1316 l_entities[i].entity_instance_num = 1317 le16toh(curr_entity->entity_instance_num); 1318 l_entities[i].entity_container_id = 1319 le16toh(curr_entity->entity_container_id); 1320 } 1321 1322 *num_entities = l_num_entities; 1323 *entities = l_entities; 1324 } 1325 1326 /* Find the position of record in pldm_pdr repo and place new_record in 1327 * the same position. 1328 */ 1329 LIBPLDM_CC_NONNULL 1330 static int pldm_pdr_replace_record(pldm_pdr *repo, pldm_pdr_record *record, 1331 pldm_pdr_record *prev, 1332 pldm_pdr_record *new_record) 1333 { 1334 if (repo->size < record->size) { 1335 return -EOVERFLOW; 1336 } 1337 1338 if (repo->size + new_record->size < new_record->size) { 1339 return -EOVERFLOW; 1340 } 1341 1342 if (repo->first == record) { 1343 repo->first = new_record; 1344 } else { 1345 prev->next = new_record; 1346 } 1347 new_record->next = record->next; 1348 1349 if (repo->last == record) { 1350 repo->last = new_record; 1351 } 1352 1353 repo->size = (repo->size - record->size) + new_record->size; 1354 return 0; 1355 } 1356 1357 /* Insert a new record to pldm_pdr repo to a position that comes after 1358 * pldm_pdr_record record. 1359 */ 1360 LIBPLDM_CC_NONNULL 1361 static int pldm_pdr_insert_record(pldm_pdr *repo, pldm_pdr_record *record, 1362 pldm_pdr_record *new_record) 1363 { 1364 if (repo->size + new_record->size < new_record->size) { 1365 return -EOVERFLOW; 1366 } 1367 1368 if (repo->record_count == UINT32_MAX) { 1369 return -EOVERFLOW; 1370 } 1371 1372 new_record->next = record->next; 1373 record->next = new_record; 1374 1375 if (repo->last == record) { 1376 repo->last = new_record; 1377 } 1378 1379 repo->size = repo->size + new_record->size; 1380 ++repo->record_count; 1381 return 0; 1382 } 1383 1384 /* Find the position of PDR when its record handle is known 1385 */ 1386 LIBPLDM_CC_NONNULL 1387 static bool pldm_pdr_find_record_by_handle(pldm_pdr_record **record, 1388 pldm_pdr_record **prev, 1389 uint32_t record_handle) 1390 { 1391 while (*record != NULL) { 1392 if ((*record)->record_handle == record_handle) { 1393 return true; 1394 } 1395 *prev = *record; 1396 *record = (*record)->next; 1397 } 1398 return false; 1399 } 1400 1401 LIBPLDM_ABI_TESTING 1402 int pldm_entity_association_pdr_add_contained_entity_to_remote_pdr( 1403 pldm_pdr *repo, pldm_entity *entity, uint32_t pdr_record_handle) 1404 { 1405 if (!repo || !entity) { 1406 return -EINVAL; 1407 } 1408 1409 pldm_pdr_record *record = repo->first; 1410 pldm_pdr_record *prev = repo->first; 1411 int rc = 0; 1412 uint16_t header_length = 0; 1413 uint8_t num_children = 0; 1414 struct pldm_msgbuf _src; 1415 struct pldm_msgbuf *src = &_src; 1416 struct pldm_msgbuf _dst; 1417 struct pldm_msgbuf *dst = &_dst; 1418 1419 pldm_pdr_find_record_by_handle(&record, &prev, pdr_record_handle); 1420 1421 if (!record) { 1422 return -EINVAL; 1423 } 1424 // Initialize msg buffer for record and record->data 1425 rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1426 record->data, record->size); 1427 if (rc) { 1428 return rc; 1429 } 1430 1431 // check if adding another entity to record causes overflow before 1432 // allocating memory for new_record. 1433 if (record->size + sizeof(pldm_entity) < sizeof(pldm_entity)) { 1434 return -EOVERFLOW; 1435 } 1436 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record)); 1437 if (!new_record) { 1438 return -ENOMEM; 1439 } 1440 1441 new_record->data = malloc(record->size + sizeof(pldm_entity)); 1442 if (!new_record->data) { 1443 rc = -ENOMEM; 1444 goto cleanup_new_record; 1445 } 1446 1447 new_record->record_handle = record->record_handle; 1448 new_record->size = record->size + sizeof(struct pldm_entity); 1449 new_record->is_remote = record->is_remote; 1450 1451 // Initialize new PDR record with data from original PDR record. 1452 // Start with adding the header of original PDR 1453 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1454 new_record->data, new_record->size); 1455 if (rc) { 1456 goto cleanup_new_record_data; 1457 } 1458 1459 pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle); 1460 pldm_msgbuf_copy(dst, src, uint8_t, hdr_version); 1461 pldm_msgbuf_copy(dst, src, uint8_t, hdr_type); 1462 pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num); 1463 // extract the header length from record and increment size with 1464 // size of pldm_entity before inserting the value into new_record. 1465 rc = pldm_msgbuf_extract(src, header_length); 1466 if (rc) { 1467 goto cleanup_new_record_data; 1468 } 1469 static_assert(UINT16_MAX < (SIZE_MAX - sizeof(pldm_entity)), 1470 "Fix the following bounds check."); 1471 if (header_length + sizeof(pldm_entity) > UINT16_MAX) { 1472 rc = -EOVERFLOW; 1473 goto cleanup_new_record_data; 1474 } 1475 header_length += sizeof(pldm_entity); 1476 pldm_msgbuf_insert(dst, header_length); 1477 pldm_msgbuf_copy(dst, src, uint16_t, container_id); 1478 pldm_msgbuf_copy(dst, src, uint8_t, association_type); 1479 pldm_msgbuf_copy(dst, src, uint16_t, entity_type); 1480 pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num); 1481 pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id); 1482 // extract value of number of children from record and increment it 1483 // by 1 before insert the value to new record. 1484 rc = pldm_msgbuf_extract(src, num_children); 1485 if (rc) { 1486 goto cleanup_new_record_data; 1487 } 1488 if (num_children == UINT8_MAX) { 1489 rc = -EOVERFLOW; 1490 goto cleanup_new_record_data; 1491 } 1492 num_children += 1; 1493 pldm_msgbuf_insert(dst, num_children); 1494 //Add all children of original PDR to new PDR 1495 for (int i = 0; i < num_children - 1; i++) { 1496 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type); 1497 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num); 1498 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id); 1499 } 1500 1501 // Add new contained entity as a child of new PDR 1502 rc = pldm_msgbuf_destroy(src); 1503 if (rc) { 1504 goto cleanup_new_record_data; 1505 } 1506 rc = pldm_msgbuf_init_errno(src, sizeof(struct pldm_entity), entity, 1507 sizeof(struct pldm_entity)); 1508 if (rc) { 1509 goto cleanup_new_record_data; 1510 } 1511 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type); 1512 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num); 1513 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id); 1514 1515 rc = pldm_msgbuf_destroy(src); 1516 if (rc) { 1517 goto cleanup_new_record_data; 1518 } 1519 rc = pldm_msgbuf_destroy(dst); 1520 if (rc) { 1521 goto cleanup_new_record_data; 1522 } 1523 1524 rc = pldm_pdr_replace_record(repo, record, prev, new_record); 1525 if (rc) { 1526 goto cleanup_new_record_data; 1527 } 1528 1529 free(record->data); 1530 free(record); 1531 return rc; 1532 cleanup_new_record_data: 1533 free(new_record->data); 1534 cleanup_new_record: 1535 free(new_record); 1536 return rc; 1537 } 1538 1539 LIBPLDM_ABI_TESTING 1540 int pldm_entity_association_pdr_create_new(pldm_pdr *repo, 1541 uint32_t pdr_record_handle, 1542 pldm_entity *parent, 1543 pldm_entity *entity, 1544 uint32_t *entity_record_handle) 1545 { 1546 if (!repo || !parent || !entity || !entity_record_handle) { 1547 return -EINVAL; 1548 } 1549 1550 if (pdr_record_handle == UINT32_MAX) { 1551 return -EOVERFLOW; 1552 } 1553 1554 bool pdr_added = false; 1555 uint16_t new_pdr_size; 1556 uint16_t container_id = 0; 1557 void *container_id_addr; 1558 struct pldm_msgbuf _dst; 1559 struct pldm_msgbuf *dst = &_dst; 1560 struct pldm_msgbuf _src_p; 1561 struct pldm_msgbuf *src_p = &_src_p; 1562 struct pldm_msgbuf _src_c; 1563 struct pldm_msgbuf *src_c = &_src_c; 1564 int rc = 0; 1565 1566 pldm_pdr_record *prev = repo->first; 1567 pldm_pdr_record *record = repo->first; 1568 pdr_added = pldm_pdr_find_record_by_handle(&record, &prev, 1569 pdr_record_handle); 1570 if (!pdr_added) { 1571 return -ENOENT; 1572 } 1573 1574 static_assert(PDR_ENTITY_ASSOCIATION_MIN_SIZE < UINT16_MAX, 1575 "Truncation ahead"); 1576 new_pdr_size = PDR_ENTITY_ASSOCIATION_MIN_SIZE; 1577 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record)); 1578 if (!new_record) { 1579 return -ENOMEM; 1580 } 1581 1582 new_record->data = malloc(new_pdr_size); 1583 if (!new_record->data) { 1584 rc = -ENOMEM; 1585 goto cleanup_new_record; 1586 } 1587 1588 // Initialise new PDR to be added with the header, size and handle. 1589 // Set the position of new PDR 1590 *entity_record_handle = pdr_record_handle + 1; 1591 new_record->record_handle = *entity_record_handle; 1592 new_record->size = new_pdr_size; 1593 new_record->is_remote = false; 1594 1595 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1596 new_record->data, new_record->size); 1597 if (rc) { 1598 goto cleanup_new_record_data; 1599 } 1600 1601 // header record handle 1602 pldm_msgbuf_insert(dst, *entity_record_handle); 1603 // header version 1604 pldm_msgbuf_insert_uint8(dst, 1); 1605 // header type 1606 pldm_msgbuf_insert_uint8(dst, PLDM_PDR_ENTITY_ASSOCIATION); 1607 // header change number 1608 pldm_msgbuf_insert_uint16(dst, 0); 1609 // header length 1610 pldm_msgbuf_insert_uint16(dst, 1611 (new_pdr_size - sizeof(struct pldm_pdr_hdr))); 1612 1613 // Data for new PDR is obtained from parent PDR and new contained entity 1614 // is added as the child 1615 rc = pldm_msgbuf_init_errno(src_p, sizeof(struct pldm_entity), parent, 1616 sizeof(*parent)); 1617 if (rc) { 1618 goto cleanup_new_record_data; 1619 } 1620 1621 rc = pldm_msgbuf_init_errno(src_c, sizeof(struct pldm_entity), entity, 1622 sizeof(*entity)); 1623 if (rc) { 1624 goto cleanup_new_record_data; 1625 } 1626 1627 container_id_addr = NULL; 1628 // extract pointer for container ID and save the address 1629 rc = pldm_msgbuf_span_required(dst, sizeof(container_id), 1630 (void **)&container_id_addr); 1631 if (rc) { 1632 goto cleanup_new_record_data; 1633 } 1634 assert(container_id_addr); 1635 pldm_msgbuf_insert_uint8(dst, PLDM_ENTITY_ASSOCIAION_PHYSICAL); 1636 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_type); 1637 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_instance_num); 1638 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_container_id); 1639 // number of children 1640 pldm_msgbuf_insert_uint8(dst, 1); 1641 1642 // Add new entity as child 1643 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_type); 1644 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_instance_num); 1645 // Extract and insert child entity container ID and add same value to 1646 // container ID of entity 1647 pldm_msgbuf_extract(src_c, container_id); 1648 pldm_msgbuf_insert(dst, container_id); 1649 container_id = htole16(container_id); 1650 memcpy(container_id_addr, &container_id, sizeof(uint16_t)); 1651 1652 rc = pldm_msgbuf_destroy(dst); 1653 if (rc) { 1654 goto cleanup_new_record_data; 1655 } 1656 rc = pldm_msgbuf_destroy(src_p); 1657 if (rc) { 1658 goto cleanup_new_record_data; 1659 } 1660 rc = pldm_msgbuf_destroy(src_c); 1661 if (rc) { 1662 goto cleanup_new_record_data; 1663 } 1664 1665 rc = pldm_pdr_insert_record(repo, record, new_record); 1666 if (rc) { 1667 goto cleanup_new_record_data; 1668 } 1669 1670 return rc; 1671 cleanup_new_record_data: 1672 free(new_record->data); 1673 cleanup_new_record: 1674 free(new_record); 1675 return rc; 1676 } 1677 1678 LIBPLDM_CC_NONNULL 1679 static bool pldm_entity_cmp(const struct pldm_entity *l, 1680 const struct pldm_entity *r) 1681 { 1682 return l->entity_type == r->entity_type && 1683 l->entity_instance_num == r->entity_instance_num && 1684 l->entity_container_id == r->entity_container_id; 1685 } 1686 1687 /* Find record handle of a PDR record from PDR repo and 1688 * entity 1689 */ 1690 LIBPLDM_CC_NONNULL 1691 static int pldm_entity_association_find_record_handle_by_entity( 1692 pldm_pdr *repo, pldm_entity *entity, bool is_remote, 1693 uint32_t *record_handle) 1694 { 1695 uint8_t num_children = 0; 1696 uint8_t hdr_type = 0; 1697 int rc = 0; 1698 size_t skip_data_size = 0; 1699 pldm_pdr_record *record = repo->first; 1700 struct pldm_msgbuf _dst; 1701 struct pldm_msgbuf *dst = &_dst; 1702 1703 while (record != NULL) { 1704 rc = pldm_msgbuf_init_errno(dst, 1705 PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1706 record->data, record->size); 1707 if (rc) { 1708 return rc; 1709 } 1710 skip_data_size = sizeof(uint32_t) + sizeof(uint8_t); 1711 pldm_msgbuf_span_required(dst, skip_data_size, NULL); 1712 pldm_msgbuf_extract(dst, hdr_type); 1713 if (record->is_remote != is_remote || 1714 hdr_type != PLDM_PDR_ENTITY_ASSOCIATION) { 1715 goto cleanup; 1716 } 1717 skip_data_size = sizeof(uint16_t) + sizeof(uint16_t) + 1718 sizeof(uint16_t) + sizeof(uint8_t) + 1719 sizeof(struct pldm_entity); 1720 pldm_msgbuf_span_required(dst, skip_data_size, NULL); 1721 pldm_msgbuf_extract(dst, num_children); 1722 for (int i = 0; i < num_children; ++i) { 1723 struct pldm_entity e; 1724 1725 if ((rc = pldm_msgbuf_extract(dst, e.entity_type)) || 1726 (rc = pldm_msgbuf_extract(dst, 1727 e.entity_instance_num)) || 1728 (rc = pldm_msgbuf_extract(dst, 1729 e.entity_container_id))) { 1730 return rc; 1731 } 1732 1733 if (pldm_entity_cmp(entity, &e)) { 1734 *record_handle = record->record_handle; 1735 return 0; 1736 } 1737 } 1738 cleanup: 1739 rc = pldm_msgbuf_destroy(dst); 1740 if (rc) { 1741 return rc; 1742 } 1743 record = record->next; 1744 } 1745 return 0; 1746 } 1747 1748 LIBPLDM_ABI_TESTING 1749 int pldm_entity_association_pdr_remove_contained_entity( 1750 pldm_pdr *repo, pldm_entity *entity, bool is_remote, 1751 uint32_t *pdr_record_handle) 1752 { 1753 uint16_t header_length = 0; 1754 uint8_t num_children = 0; 1755 struct pldm_msgbuf _src; 1756 struct pldm_msgbuf *src = &_src; 1757 struct pldm_msgbuf _dst; 1758 struct pldm_msgbuf *dst = &_dst; 1759 int rc; 1760 pldm_pdr_record *record; 1761 pldm_pdr_record *prev; 1762 1763 if (!repo || !entity || !pdr_record_handle) { 1764 return -EINVAL; 1765 } 1766 record = repo->first; 1767 prev = repo->first; 1768 1769 rc = pldm_entity_association_find_record_handle_by_entity( 1770 repo, entity, is_remote, pdr_record_handle); 1771 if (rc) { 1772 return rc; 1773 } 1774 pldm_pdr_find_record_by_handle(&record, &prev, *pdr_record_handle); 1775 if (!record) { 1776 return -EINVAL; 1777 } 1778 // Initialize msg buffer for record and record->data 1779 rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1780 record->data, record->size); 1781 if (rc) { 1782 return rc; 1783 } 1784 // check if removing an entity from record causes overflow before 1785 // allocating memory for new_record. 1786 if (record->size < sizeof(pldm_entity)) { 1787 return -EOVERFLOW; 1788 } 1789 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record)); 1790 if (!new_record) { 1791 return -ENOMEM; 1792 } 1793 new_record->data = malloc(record->size - sizeof(pldm_entity)); 1794 if (!new_record->data) { 1795 rc = -ENOMEM; 1796 goto cleanup_new_record; 1797 } 1798 new_record->record_handle = record->record_handle; 1799 new_record->size = record->size - sizeof(struct pldm_entity); 1800 new_record->is_remote = record->is_remote; 1801 1802 // Initialize new PDR record with data from original PDR record. 1803 // Start with adding the header of original PDR 1804 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1805 new_record->data, new_record->size); 1806 if (rc) { 1807 goto cleanup_new_record_data; 1808 } 1809 pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle); 1810 pldm_msgbuf_copy(dst, src, uint8_t, hdr_version); 1811 pldm_msgbuf_copy(dst, src, uint8_t, hdr_type); 1812 pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num); 1813 // extract the header length from record and decrement size with 1814 // size of pldm_entity before inserting the value into new_record. 1815 rc = pldm_msgbuf_extract(src, header_length); 1816 if (rc) { 1817 goto cleanup_new_record_data; 1818 } 1819 if (header_length < sizeof(pldm_entity)) { 1820 rc = -EOVERFLOW; 1821 goto cleanup_new_record_data; 1822 } 1823 header_length -= sizeof(pldm_entity); 1824 pldm_msgbuf_insert(dst, header_length); 1825 pldm_msgbuf_copy(dst, src, uint16_t, container_id); 1826 pldm_msgbuf_copy(dst, src, uint8_t, association_type); 1827 pldm_msgbuf_copy(dst, src, uint16_t, entity_type); 1828 pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num); 1829 pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id); 1830 // extract value of number of children from record and decrement it 1831 // by 1 before insert the value to new record. 1832 rc = pldm_msgbuf_extract(src, num_children); 1833 if (rc) { 1834 goto cleanup_new_record_data; 1835 } 1836 if (num_children == 1) { 1837 prev->next = record->next; 1838 free(record->data); 1839 free(record); 1840 goto cleanup_new_record_data; 1841 } else if (num_children < 1) { 1842 rc = -EOVERFLOW; 1843 goto cleanup_new_record_data; 1844 } 1845 num_children -= 1; 1846 pldm_msgbuf_insert(dst, num_children); 1847 //Add all children of original PDR to new PDR 1848 for (int i = 0; i < num_children + 1; ++i) { 1849 struct pldm_entity e; 1850 1851 if ((rc = pldm_msgbuf_extract(src, e.entity_type)) || 1852 (rc = pldm_msgbuf_extract(src, e.entity_instance_num)) || 1853 (rc = pldm_msgbuf_extract(src, e.entity_container_id))) { 1854 goto cleanup_new_record_data; 1855 } 1856 1857 if (pldm_entity_cmp(entity, &e)) { 1858 continue; 1859 } 1860 1861 pldm_msgbuf_insert(dst, e.entity_type); 1862 pldm_msgbuf_insert(dst, e.entity_instance_num); 1863 pldm_msgbuf_insert(dst, e.entity_container_id); 1864 } 1865 1866 if ((rc = pldm_msgbuf_destroy(src)) || 1867 (rc = pldm_msgbuf_destroy(dst)) || 1868 (rc = pldm_pdr_replace_record(repo, record, prev, new_record))) { 1869 goto cleanup_new_record_data; 1870 } 1871 1872 free(record->data); 1873 free(record); 1874 return rc; 1875 1876 cleanup_new_record_data: 1877 free(new_record->data); 1878 cleanup_new_record: 1879 free(new_record); 1880 return rc; 1881 } 1882 1883 /* API to find the PDR record that is previous to a given PLDM PDR 1884 * record in a given PLDM PDR repository 1885 */ 1886 LIBPLDM_CC_NONNULL 1887 static pldm_pdr_record *pldm_pdr_get_prev_record(pldm_pdr *repo, 1888 pldm_pdr_record *record) 1889 { 1890 pldm_pdr_record *prev = NULL; 1891 pldm_pdr_record *curr = repo->first; 1892 1893 while (curr != NULL) { 1894 if (curr->record_handle == record->record_handle) { 1895 break; 1896 } 1897 prev = curr; 1898 curr = curr->next; 1899 } 1900 return prev; 1901 } 1902 1903 /* API to check if a PLDM PDR record is present in a PLDM PDR repository 1904 */ 1905 LIBPLDM_CC_NONNULL 1906 static bool is_prev_record_present(pldm_pdr *repo, pldm_pdr_record *record) 1907 { 1908 if (repo->first == record) { 1909 return true; 1910 } 1911 1912 return pldm_pdr_get_prev_record(repo, record) != NULL; 1913 } 1914 1915 /* API to check if FRU RSI of record matches the given record set identifier. 1916 * Returns 1 if the provided FRU record matches the provided record set identifier, 1917 * 0 if it does not, otherwise -EINVAL if the arguments are invalid. 1918 */ 1919 LIBPLDM_CC_NONNULL 1920 static int pldm_pdr_record_matches_fru_rsi(const pldm_pdr_record *record, 1921 uint16_t rsi) 1922 { 1923 uint16_t record_fru_rsi = 0; 1924 uint8_t *skip_data = NULL; 1925 uint8_t skip_data_size = 0; 1926 struct pldm_msgbuf _dst; 1927 struct pldm_msgbuf *dst = &_dst; 1928 int rc = 0; 1929 1930 rc = pldm_msgbuf_init_errno(dst, PDR_FRU_RECORD_SET_MIN_SIZE, 1931 record->data, record->size); 1932 if (rc) { 1933 return rc; 1934 } 1935 skip_data_size = sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t); 1936 pldm_msgbuf_span_required(dst, skip_data_size, (void **)&skip_data); 1937 pldm_msgbuf_extract(dst, record_fru_rsi); 1938 1939 rc = pldm_msgbuf_destroy(dst); 1940 if (rc) { 1941 return rc; 1942 } 1943 return record_fru_rsi == rsi; 1944 } 1945 1946 /* API to remove PLDM PDR record from a PLDM PDR repository 1947 */ 1948 LIBPLDM_CC_NONNULL_ARGS(1, 2) 1949 static int pldm_pdr_remove_record(pldm_pdr *repo, pldm_pdr_record *record, 1950 pldm_pdr_record *prev) 1951 { 1952 if (!is_prev_record_present(repo, record)) { 1953 return -EINVAL; 1954 } 1955 1956 assert(repo->size >= record->size); 1957 if (repo->size < record->size) { 1958 return -EOVERFLOW; 1959 } 1960 1961 if (repo->first == record) { 1962 repo->first = record->next; 1963 } else { 1964 if (prev != NULL) { 1965 prev->next = record->next; 1966 } 1967 } 1968 1969 if (repo->last == record) { 1970 repo->last = prev; 1971 if (prev != NULL) { 1972 prev->next = NULL; 1973 } 1974 } 1975 repo->record_count -= 1; 1976 repo->size -= record->size; 1977 free(record->data); 1978 free(record); 1979 1980 return 0; 1981 } 1982 1983 LIBPLDM_ABI_TESTING 1984 int pldm_pdr_remove_fru_record_set_by_rsi(pldm_pdr *repo, uint16_t fru_rsi, 1985 bool is_remote, 1986 uint32_t *record_handle) 1987 { 1988 pldm_pdr_record *record; 1989 pldm_pdr_record *prev = NULL; 1990 size_t skip_data_size = sizeof(uint32_t) + sizeof(uint8_t); 1991 uint8_t hdr_type = 0; 1992 int rc = 0; 1993 int match; 1994 1995 if (!repo || !record_handle) { 1996 return -EINVAL; 1997 } 1998 record = repo->first; 1999 2000 while (record != NULL) { 2001 struct pldm_msgbuf _buf; 2002 struct pldm_msgbuf *buf = &_buf; 2003 rc = pldm_msgbuf_init_errno(buf, PDR_FRU_RECORD_SET_MIN_SIZE, 2004 record->data, record->size); 2005 if (rc) { 2006 return rc; 2007 } 2008 pldm_msgbuf_span_required(buf, skip_data_size, NULL); 2009 if ((rc = pldm_msgbuf_extract(buf, hdr_type))) { 2010 return rc; 2011 } 2012 if (record->is_remote != is_remote || 2013 hdr_type != PLDM_PDR_FRU_RECORD_SET) { 2014 goto cleanup; 2015 } 2016 match = pldm_pdr_record_matches_fru_rsi(record, fru_rsi); 2017 if (match < 0) { 2018 return match; 2019 } 2020 if (match) { 2021 *record_handle = record->record_handle; 2022 prev = pldm_pdr_get_prev_record(repo, record); 2023 return pldm_pdr_remove_record(repo, record, prev); 2024 } 2025 cleanup: 2026 rc = pldm_msgbuf_destroy(buf); 2027 if (rc) { 2028 return rc; 2029 } 2030 record = record->next; 2031 } 2032 return rc; 2033 } 2034