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 entity_association_pdr_add(node, repo, entities, num_entities, 974 is_remote, terminus_handle, record_handle); 975 976 return 0; 977 } 978 979 static void find_entity_ref_in_tree(pldm_entity_node *tree_node, 980 pldm_entity entity, pldm_entity_node **node) 981 { 982 bool is_entity_container_id; 983 bool is_entity_instance_num; 984 bool is_type; 985 986 if (tree_node == NULL) { 987 return; 988 } 989 990 is_type = tree_node->entity.entity_type == entity.entity_type; 991 is_entity_instance_num = tree_node->entity.entity_instance_num == 992 entity.entity_instance_num; 993 is_entity_container_id = tree_node->entity.entity_container_id == 994 entity.entity_container_id; 995 996 if (is_type && is_entity_instance_num && is_entity_container_id) { 997 *node = tree_node; 998 return; 999 } 1000 1001 find_entity_ref_in_tree(tree_node->first_child, entity, node); 1002 find_entity_ref_in_tree(tree_node->next_sibling, entity, node); 1003 } 1004 1005 LIBPLDM_ABI_STABLE 1006 void pldm_find_entity_ref_in_tree(pldm_entity_association_tree *tree, 1007 pldm_entity entity, pldm_entity_node **node) 1008 { 1009 if (!tree || !node) { 1010 return; 1011 } 1012 1013 find_entity_ref_in_tree(tree->root, entity, node); 1014 } 1015 1016 LIBPLDM_ABI_STABLE 1017 void pldm_pdr_remove_pdrs_by_terminus_handle(pldm_pdr *repo, 1018 uint16_t terminus_handle) 1019 { 1020 if (!repo) { 1021 return; 1022 } 1023 1024 bool removed = false; 1025 1026 pldm_pdr_record *record = repo->first; 1027 pldm_pdr_record *prev = NULL; 1028 while (record != NULL) { 1029 pldm_pdr_record *next = record->next; 1030 if (record->terminus_handle == terminus_handle) { 1031 if (repo->first == record) { 1032 repo->first = next; 1033 } else { 1034 prev->next = next; 1035 } 1036 if (repo->last == record) { 1037 repo->last = prev; 1038 } 1039 if (record->data) { 1040 free(record->data); 1041 } 1042 --repo->record_count; 1043 repo->size -= record->size; 1044 free(record); 1045 removed = true; 1046 } else { 1047 prev = record; 1048 } 1049 record = next; 1050 } 1051 1052 if (removed == true) { 1053 record = repo->first; 1054 uint32_t record_handle = 0; 1055 while (record != NULL) { 1056 record->record_handle = ++record_handle; 1057 if (record->data != NULL) { 1058 struct pldm_pdr_hdr *hdr = 1059 (struct pldm_pdr_hdr *)(record->data); 1060 hdr->record_handle = 1061 htole32(record->record_handle); 1062 } 1063 record = record->next; 1064 } 1065 } 1066 } 1067 1068 LIBPLDM_ABI_STABLE 1069 void pldm_pdr_remove_remote_pdrs(pldm_pdr *repo) 1070 { 1071 if (!repo) { 1072 return; 1073 } 1074 1075 bool removed = false; 1076 1077 pldm_pdr_record *record = repo->first; 1078 pldm_pdr_record *prev = NULL; 1079 while (record != NULL) { 1080 pldm_pdr_record *next = record->next; 1081 if (record->is_remote == true) { 1082 if (repo->first == record) { 1083 repo->first = next; 1084 } else { 1085 prev->next = next; 1086 } 1087 if (repo->last == record) { 1088 repo->last = prev; 1089 } 1090 if (record->data) { 1091 free(record->data); 1092 } 1093 --repo->record_count; 1094 repo->size -= record->size; 1095 free(record); 1096 removed = true; 1097 } else { 1098 prev = record; 1099 } 1100 record = next; 1101 } 1102 1103 if (removed == true) { 1104 record = repo->first; 1105 uint32_t record_handle = 0; 1106 while (record != NULL) { 1107 record->record_handle = ++record_handle; 1108 if (record->data != NULL) { 1109 struct pldm_pdr_hdr *hdr = 1110 (struct pldm_pdr_hdr *)(record->data); 1111 hdr->record_handle = 1112 htole32(record->record_handle); 1113 } 1114 record = record->next; 1115 } 1116 } 1117 } 1118 1119 LIBPLDM_ABI_STABLE 1120 pldm_pdr_record *pldm_pdr_find_last_in_range(const pldm_pdr *repo, 1121 uint32_t first, uint32_t last) 1122 { 1123 pldm_pdr_record *record = NULL; 1124 pldm_pdr_record *curr; 1125 1126 if (!repo) { 1127 return NULL; 1128 } 1129 for (curr = repo->first; curr; curr = curr->next) { 1130 if (first > curr->record_handle || last < curr->record_handle) { 1131 continue; 1132 } 1133 if (!record || curr->record_handle > record->record_handle) { 1134 record = curr; 1135 } 1136 } 1137 1138 return record; 1139 } 1140 1141 static void entity_association_tree_find_if_remote(pldm_entity_node *node, 1142 pldm_entity *entity, 1143 pldm_entity_node **out, 1144 bool is_remote) 1145 { 1146 if (node == NULL) { 1147 return; 1148 } 1149 bool is_entity_type; 1150 bool is_entity_instance_num; 1151 1152 is_entity_type = node->entity.entity_type == entity->entity_type; 1153 is_entity_instance_num = node->entity.entity_instance_num == 1154 entity->entity_instance_num; 1155 1156 if (!is_remote || 1157 node->remote_container_id == entity->entity_container_id) { 1158 if (is_entity_type && is_entity_instance_num) { 1159 entity->entity_container_id = 1160 node->entity.entity_container_id; 1161 *out = node; 1162 return; 1163 } 1164 } 1165 entity_association_tree_find_if_remote(node->next_sibling, entity, out, 1166 is_remote); 1167 entity_association_tree_find_if_remote(node->first_child, entity, out, 1168 is_remote); 1169 } 1170 1171 LIBPLDM_ABI_STABLE 1172 pldm_entity_node *pldm_entity_association_tree_find_with_locality( 1173 pldm_entity_association_tree *tree, pldm_entity *entity, bool is_remote) 1174 { 1175 if (!tree || !entity) { 1176 return NULL; 1177 } 1178 pldm_entity_node *node = NULL; 1179 entity_association_tree_find_if_remote(tree->root, entity, &node, 1180 is_remote); 1181 return node; 1182 } 1183 1184 static void entity_association_tree_find(pldm_entity_node *node, 1185 pldm_entity *entity, 1186 pldm_entity_node **out) 1187 { 1188 if (node == NULL) { 1189 return; 1190 } 1191 1192 if (node->entity.entity_type == entity->entity_type && 1193 node->entity.entity_instance_num == entity->entity_instance_num) { 1194 entity->entity_container_id = node->entity.entity_container_id; 1195 *out = node; 1196 return; 1197 } 1198 entity_association_tree_find(node->next_sibling, entity, out); 1199 entity_association_tree_find(node->first_child, entity, out); 1200 } 1201 1202 LIBPLDM_ABI_STABLE 1203 pldm_entity_node * 1204 pldm_entity_association_tree_find(pldm_entity_association_tree *tree, 1205 pldm_entity *entity) 1206 { 1207 if (!tree || !entity) { 1208 return NULL; 1209 } 1210 1211 pldm_entity_node *node = NULL; 1212 entity_association_tree_find(tree->root, entity, &node); 1213 return node; 1214 } 1215 1216 static int entity_association_tree_copy(pldm_entity_node *org_node, 1217 pldm_entity_node **new_node) 1218 { 1219 int rc; 1220 1221 if (org_node == NULL) { 1222 return 0; 1223 } 1224 1225 *new_node = malloc(sizeof(pldm_entity_node)); 1226 if (!*new_node) { 1227 return -ENOMEM; 1228 } 1229 1230 (*new_node)->parent = org_node->parent; 1231 (*new_node)->entity = org_node->entity; 1232 (*new_node)->association_type = org_node->association_type; 1233 (*new_node)->remote_container_id = org_node->remote_container_id; 1234 (*new_node)->first_child = NULL; 1235 (*new_node)->next_sibling = NULL; 1236 1237 rc = entity_association_tree_copy(org_node->first_child, 1238 &((*new_node)->first_child)); 1239 if (rc) { 1240 goto cleanup; 1241 } 1242 1243 rc = entity_association_tree_copy(org_node->next_sibling, 1244 &((*new_node)->next_sibling)); 1245 if (rc) { 1246 entity_association_tree_destroy((*new_node)->first_child); 1247 goto cleanup; 1248 } 1249 1250 return 0; 1251 1252 cleanup: 1253 free(*new_node); 1254 *new_node = NULL; 1255 return rc; 1256 } 1257 1258 LIBPLDM_ABI_DEPRECATED 1259 void pldm_entity_association_tree_copy_root( 1260 pldm_entity_association_tree *org_tree, 1261 pldm_entity_association_tree *new_tree) 1262 { 1263 assert(org_tree != NULL); 1264 assert(new_tree != NULL); 1265 1266 new_tree->last_used_container_id = org_tree->last_used_container_id; 1267 entity_association_tree_copy(org_tree->root, &(new_tree->root)); 1268 } 1269 1270 LIBPLDM_ABI_TESTING 1271 int pldm_entity_association_tree_copy_root_check( 1272 pldm_entity_association_tree *org_tree, 1273 pldm_entity_association_tree *new_tree) 1274 { 1275 if (!org_tree || !new_tree) { 1276 return -EINVAL; 1277 } 1278 1279 new_tree->last_used_container_id = org_tree->last_used_container_id; 1280 return entity_association_tree_copy(org_tree->root, &(new_tree->root)); 1281 } 1282 1283 LIBPLDM_ABI_STABLE 1284 void pldm_entity_association_tree_destroy_root( 1285 pldm_entity_association_tree *tree) 1286 { 1287 if (!tree) { 1288 return; 1289 } 1290 1291 entity_association_tree_destroy(tree->root); 1292 tree->last_used_container_id = 0; 1293 tree->root = NULL; 1294 } 1295 1296 LIBPLDM_ABI_STABLE 1297 bool pldm_is_empty_entity_assoc_tree(pldm_entity_association_tree *tree) 1298 { 1299 return ((tree->root == NULL) ? true : false); 1300 } 1301 1302 LIBPLDM_ABI_STABLE 1303 void pldm_entity_association_pdr_extract(const uint8_t *pdr, uint16_t pdr_len, 1304 size_t *num_entities, 1305 pldm_entity **entities) 1306 { 1307 if (!pdr || !num_entities || !entities) { 1308 return; 1309 } 1310 if (pdr_len < PDR_ENTITY_ASSOCIATION_MIN_SIZE) { 1311 return; 1312 } 1313 1314 struct pldm_pdr_hdr *hdr = (struct pldm_pdr_hdr *)pdr; 1315 if (hdr->type != PLDM_PDR_ENTITY_ASSOCIATION) { 1316 return; 1317 } 1318 1319 const uint8_t *start = (uint8_t *)pdr; 1320 const uint8_t *end LIBPLDM_CC_UNUSED = 1321 start + sizeof(struct pldm_pdr_hdr) + le16toh(hdr->length); 1322 start += sizeof(struct pldm_pdr_hdr); 1323 struct pldm_pdr_entity_association *entity_association_pdr = 1324 (struct pldm_pdr_entity_association *)start; 1325 size_t l_num_entities = entity_association_pdr->num_children + 1; 1326 if (l_num_entities < 2) { 1327 return; 1328 } 1329 if (start + sizeof(struct pldm_pdr_entity_association) + 1330 sizeof(pldm_entity) * (l_num_entities - 2) != 1331 end) { 1332 return; 1333 } 1334 pldm_entity *l_entities = malloc(sizeof(pldm_entity) * l_num_entities); 1335 if (!l_entities) { 1336 return; 1337 } 1338 l_entities[0].entity_type = 1339 le16toh(entity_association_pdr->container.entity_type); 1340 l_entities[0].entity_instance_num = 1341 le16toh(entity_association_pdr->container.entity_instance_num); 1342 l_entities[0].entity_container_id = 1343 le16toh(entity_association_pdr->container.entity_container_id); 1344 pldm_entity *curr_entity = entity_association_pdr->children; 1345 for (size_t i = 1; i < l_num_entities; i++, curr_entity++) { 1346 l_entities[i].entity_type = le16toh(curr_entity->entity_type); 1347 l_entities[i].entity_instance_num = 1348 le16toh(curr_entity->entity_instance_num); 1349 l_entities[i].entity_container_id = 1350 le16toh(curr_entity->entity_container_id); 1351 } 1352 1353 *num_entities = l_num_entities; 1354 *entities = l_entities; 1355 } 1356 1357 /* Find the position of record in pldm_pdr repo and place new_record in 1358 * the same position. 1359 */ 1360 LIBPLDM_CC_NONNULL 1361 static int pldm_pdr_replace_record(pldm_pdr *repo, pldm_pdr_record *record, 1362 pldm_pdr_record *prev, 1363 pldm_pdr_record *new_record) 1364 { 1365 if (repo->size < record->size) { 1366 return -EOVERFLOW; 1367 } 1368 1369 if (repo->size + new_record->size < new_record->size) { 1370 return -EOVERFLOW; 1371 } 1372 1373 if (repo->first == record) { 1374 repo->first = new_record; 1375 } else { 1376 prev->next = new_record; 1377 } 1378 new_record->next = record->next; 1379 1380 if (repo->last == record) { 1381 repo->last = new_record; 1382 } 1383 1384 repo->size = (repo->size - record->size) + new_record->size; 1385 return 0; 1386 } 1387 1388 /* Insert a new record to pldm_pdr repo to a position that comes after 1389 * pldm_pdr_record record. 1390 */ 1391 LIBPLDM_CC_NONNULL 1392 static int pldm_pdr_insert_record(pldm_pdr *repo, pldm_pdr_record *record, 1393 pldm_pdr_record *new_record) 1394 { 1395 if (repo->size + new_record->size < new_record->size) { 1396 return -EOVERFLOW; 1397 } 1398 1399 if (repo->record_count == UINT32_MAX) { 1400 return -EOVERFLOW; 1401 } 1402 1403 new_record->next = record->next; 1404 record->next = new_record; 1405 1406 if (repo->last == record) { 1407 repo->last = new_record; 1408 } 1409 1410 repo->size = repo->size + new_record->size; 1411 ++repo->record_count; 1412 return 0; 1413 } 1414 1415 /* Find the position of PDR when its record handle is known 1416 */ 1417 LIBPLDM_CC_NONNULL 1418 static bool pldm_pdr_find_record_by_handle(pldm_pdr_record **record, 1419 pldm_pdr_record **prev, 1420 uint32_t record_handle) 1421 { 1422 while (*record != NULL) { 1423 if ((*record)->record_handle == record_handle) { 1424 return true; 1425 } 1426 *prev = *record; 1427 *record = (*record)->next; 1428 } 1429 return false; 1430 } 1431 1432 LIBPLDM_ABI_TESTING 1433 int pldm_entity_association_pdr_add_contained_entity_to_remote_pdr( 1434 pldm_pdr *repo, pldm_entity *entity, uint32_t pdr_record_handle) 1435 { 1436 if (!repo || !entity) { 1437 return -EINVAL; 1438 } 1439 1440 pldm_pdr_record *record = repo->first; 1441 pldm_pdr_record *prev = repo->first; 1442 int rc = 0; 1443 uint16_t header_length = 0; 1444 uint8_t num_children = 0; 1445 struct pldm_msgbuf _src; 1446 struct pldm_msgbuf *src = &_src; 1447 struct pldm_msgbuf _dst; 1448 struct pldm_msgbuf *dst = &_dst; 1449 1450 pldm_pdr_find_record_by_handle(&record, &prev, pdr_record_handle); 1451 1452 if (!record) { 1453 return -EINVAL; 1454 } 1455 // Initialize msg buffer for record and record->data 1456 rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1457 record->data, record->size); 1458 if (rc) { 1459 return rc; 1460 } 1461 1462 // check if adding another entity to record causes overflow before 1463 // allocating memory for new_record. 1464 if (record->size + sizeof(pldm_entity) < sizeof(pldm_entity)) { 1465 return -EOVERFLOW; 1466 } 1467 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record)); 1468 if (!new_record) { 1469 return -ENOMEM; 1470 } 1471 1472 new_record->data = malloc(record->size + sizeof(pldm_entity)); 1473 if (!new_record->data) { 1474 rc = -ENOMEM; 1475 goto cleanup_new_record; 1476 } 1477 1478 new_record->record_handle = record->record_handle; 1479 new_record->size = record->size + sizeof(struct pldm_entity); 1480 new_record->is_remote = record->is_remote; 1481 1482 // Initialize new PDR record with data from original PDR record. 1483 // Start with adding the header of original PDR 1484 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1485 new_record->data, new_record->size); 1486 if (rc) { 1487 goto cleanup_new_record_data; 1488 } 1489 1490 pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle); 1491 pldm_msgbuf_copy(dst, src, uint8_t, hdr_version); 1492 pldm_msgbuf_copy(dst, src, uint8_t, hdr_type); 1493 pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num); 1494 // extract the header length from record and increment size with 1495 // size of pldm_entity before inserting the value into new_record. 1496 rc = pldm_msgbuf_extract(src, header_length); 1497 if (rc) { 1498 goto cleanup_new_record_data; 1499 } 1500 static_assert(UINT16_MAX < (SIZE_MAX - sizeof(pldm_entity)), 1501 "Fix the following bounds check."); 1502 if (header_length + sizeof(pldm_entity) > UINT16_MAX) { 1503 rc = -EOVERFLOW; 1504 goto cleanup_new_record_data; 1505 } 1506 header_length += sizeof(pldm_entity); 1507 pldm_msgbuf_insert(dst, header_length); 1508 pldm_msgbuf_copy(dst, src, uint16_t, container_id); 1509 pldm_msgbuf_copy(dst, src, uint8_t, association_type); 1510 pldm_msgbuf_copy(dst, src, uint16_t, entity_type); 1511 pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num); 1512 pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id); 1513 // extract value of number of children from record and increment it 1514 // by 1 before insert the value to new record. 1515 rc = pldm_msgbuf_extract(src, num_children); 1516 if (rc) { 1517 goto cleanup_new_record_data; 1518 } 1519 if (num_children == UINT8_MAX) { 1520 rc = -EOVERFLOW; 1521 goto cleanup_new_record_data; 1522 } 1523 num_children += 1; 1524 pldm_msgbuf_insert(dst, num_children); 1525 //Add all children of original PDR to new PDR 1526 for (int i = 0; i < num_children - 1; i++) { 1527 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type); 1528 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num); 1529 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id); 1530 } 1531 1532 // Add new contained entity as a child of new PDR 1533 rc = pldm_msgbuf_destroy(src); 1534 if (rc) { 1535 goto cleanup_new_record_data; 1536 } 1537 rc = pldm_msgbuf_init_errno(src, sizeof(struct pldm_entity), entity, 1538 sizeof(struct pldm_entity)); 1539 if (rc) { 1540 goto cleanup_new_record_data; 1541 } 1542 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type); 1543 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num); 1544 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id); 1545 1546 rc = pldm_msgbuf_destroy(src); 1547 if (rc) { 1548 goto cleanup_new_record_data; 1549 } 1550 rc = pldm_msgbuf_destroy(dst); 1551 if (rc) { 1552 goto cleanup_new_record_data; 1553 } 1554 1555 rc = pldm_pdr_replace_record(repo, record, prev, new_record); 1556 if (rc) { 1557 goto cleanup_new_record_data; 1558 } 1559 1560 free(record->data); 1561 free(record); 1562 return rc; 1563 cleanup_new_record_data: 1564 free(new_record->data); 1565 cleanup_new_record: 1566 free(new_record); 1567 return rc; 1568 } 1569 1570 LIBPLDM_ABI_TESTING 1571 int pldm_entity_association_pdr_create_new(pldm_pdr *repo, 1572 uint32_t pdr_record_handle, 1573 pldm_entity *parent, 1574 pldm_entity *entity, 1575 uint32_t *entity_record_handle) 1576 { 1577 if (!repo || !parent || !entity || !entity_record_handle) { 1578 return -EINVAL; 1579 } 1580 1581 if (pdr_record_handle == UINT32_MAX) { 1582 return -EOVERFLOW; 1583 } 1584 1585 bool pdr_added = false; 1586 uint16_t new_pdr_size; 1587 uint16_t container_id = 0; 1588 void *container_id_addr; 1589 struct pldm_msgbuf _dst; 1590 struct pldm_msgbuf *dst = &_dst; 1591 struct pldm_msgbuf _src_p; 1592 struct pldm_msgbuf *src_p = &_src_p; 1593 struct pldm_msgbuf _src_c; 1594 struct pldm_msgbuf *src_c = &_src_c; 1595 int rc = 0; 1596 1597 pldm_pdr_record *prev = repo->first; 1598 pldm_pdr_record *record = repo->first; 1599 pdr_added = pldm_pdr_find_record_by_handle(&record, &prev, 1600 pdr_record_handle); 1601 if (!pdr_added) { 1602 return -ENOENT; 1603 } 1604 1605 static_assert(PDR_ENTITY_ASSOCIATION_MIN_SIZE < UINT16_MAX, 1606 "Truncation ahead"); 1607 new_pdr_size = PDR_ENTITY_ASSOCIATION_MIN_SIZE; 1608 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record)); 1609 if (!new_record) { 1610 return -ENOMEM; 1611 } 1612 1613 new_record->data = malloc(new_pdr_size); 1614 if (!new_record->data) { 1615 rc = -ENOMEM; 1616 goto cleanup_new_record; 1617 } 1618 1619 // Initialise new PDR to be added with the header, size and handle. 1620 // Set the position of new PDR 1621 *entity_record_handle = pdr_record_handle + 1; 1622 new_record->record_handle = *entity_record_handle; 1623 new_record->size = new_pdr_size; 1624 new_record->is_remote = false; 1625 1626 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1627 new_record->data, new_record->size); 1628 if (rc) { 1629 goto cleanup_new_record_data; 1630 } 1631 1632 // header record handle 1633 pldm_msgbuf_insert(dst, *entity_record_handle); 1634 // header version 1635 pldm_msgbuf_insert_uint8(dst, 1); 1636 // header type 1637 pldm_msgbuf_insert_uint8(dst, PLDM_PDR_ENTITY_ASSOCIATION); 1638 // header change number 1639 pldm_msgbuf_insert_uint16(dst, 0); 1640 // header length 1641 pldm_msgbuf_insert_uint16(dst, 1642 (new_pdr_size - sizeof(struct pldm_pdr_hdr))); 1643 1644 // Data for new PDR is obtained from parent PDR and new contained entity 1645 // is added as the child 1646 rc = pldm_msgbuf_init_errno(src_p, sizeof(struct pldm_entity), parent, 1647 sizeof(*parent)); 1648 if (rc) { 1649 goto cleanup_new_record_data; 1650 } 1651 1652 rc = pldm_msgbuf_init_errno(src_c, sizeof(struct pldm_entity), entity, 1653 sizeof(*entity)); 1654 if (rc) { 1655 goto cleanup_new_record_data; 1656 } 1657 1658 container_id_addr = NULL; 1659 // extract pointer for container ID and save the address 1660 rc = pldm_msgbuf_span_required(dst, sizeof(container_id), 1661 (void **)&container_id_addr); 1662 if (rc) { 1663 goto cleanup_new_record_data; 1664 } 1665 assert(container_id_addr); 1666 pldm_msgbuf_insert_uint8(dst, PLDM_ENTITY_ASSOCIAION_PHYSICAL); 1667 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_type); 1668 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_instance_num); 1669 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_container_id); 1670 // number of children 1671 pldm_msgbuf_insert_uint8(dst, 1); 1672 1673 // Add new entity as child 1674 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_type); 1675 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_instance_num); 1676 // Extract and insert child entity container ID and add same value to 1677 // container ID of entity 1678 pldm_msgbuf_extract(src_c, container_id); 1679 pldm_msgbuf_insert(dst, container_id); 1680 container_id = htole16(container_id); 1681 memcpy(container_id_addr, &container_id, sizeof(uint16_t)); 1682 1683 rc = pldm_msgbuf_destroy(dst); 1684 if (rc) { 1685 goto cleanup_new_record_data; 1686 } 1687 rc = pldm_msgbuf_destroy(src_p); 1688 if (rc) { 1689 goto cleanup_new_record_data; 1690 } 1691 rc = pldm_msgbuf_destroy(src_c); 1692 if (rc) { 1693 goto cleanup_new_record_data; 1694 } 1695 1696 rc = pldm_pdr_insert_record(repo, record, new_record); 1697 if (rc) { 1698 goto cleanup_new_record_data; 1699 } 1700 1701 return rc; 1702 cleanup_new_record_data: 1703 free(new_record->data); 1704 cleanup_new_record: 1705 free(new_record); 1706 return rc; 1707 } 1708 1709 LIBPLDM_CC_NONNULL 1710 static bool pldm_entity_cmp(const struct pldm_entity *l, 1711 const struct pldm_entity *r) 1712 { 1713 return l->entity_type == r->entity_type && 1714 l->entity_instance_num == r->entity_instance_num && 1715 l->entity_container_id == r->entity_container_id; 1716 } 1717 1718 /* Find record handle of a PDR record from PDR repo and 1719 * entity 1720 */ 1721 LIBPLDM_CC_NONNULL 1722 static int pldm_entity_association_find_record_handle_by_entity( 1723 pldm_pdr *repo, pldm_entity *entity, bool is_remote, 1724 uint32_t *record_handle) 1725 { 1726 uint8_t num_children = 0; 1727 uint8_t hdr_type = 0; 1728 int rc = 0; 1729 size_t skip_data_size = 0; 1730 pldm_pdr_record *record = repo->first; 1731 struct pldm_msgbuf _dst; 1732 struct pldm_msgbuf *dst = &_dst; 1733 1734 while (record != NULL) { 1735 rc = pldm_msgbuf_init_errno(dst, 1736 PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1737 record->data, record->size); 1738 if (rc) { 1739 return rc; 1740 } 1741 skip_data_size = sizeof(uint32_t) + sizeof(uint8_t); 1742 pldm_msgbuf_span_required(dst, skip_data_size, NULL); 1743 pldm_msgbuf_extract(dst, hdr_type); 1744 if (record->is_remote != is_remote || 1745 hdr_type != PLDM_PDR_ENTITY_ASSOCIATION) { 1746 goto cleanup; 1747 } 1748 skip_data_size = sizeof(uint16_t) + sizeof(uint16_t) + 1749 sizeof(uint16_t) + sizeof(uint8_t) + 1750 sizeof(struct pldm_entity); 1751 pldm_msgbuf_span_required(dst, skip_data_size, NULL); 1752 pldm_msgbuf_extract(dst, num_children); 1753 for (int i = 0; i < num_children; ++i) { 1754 struct pldm_entity e; 1755 1756 if ((rc = pldm_msgbuf_extract(dst, e.entity_type)) || 1757 (rc = pldm_msgbuf_extract(dst, 1758 e.entity_instance_num)) || 1759 (rc = pldm_msgbuf_extract(dst, 1760 e.entity_container_id))) { 1761 return rc; 1762 } 1763 1764 if (pldm_entity_cmp(entity, &e)) { 1765 *record_handle = record->record_handle; 1766 return 0; 1767 } 1768 } 1769 cleanup: 1770 rc = pldm_msgbuf_destroy(dst); 1771 if (rc) { 1772 return rc; 1773 } 1774 record = record->next; 1775 } 1776 return 0; 1777 } 1778 1779 LIBPLDM_ABI_TESTING 1780 int pldm_entity_association_pdr_remove_contained_entity( 1781 pldm_pdr *repo, pldm_entity *entity, bool is_remote, 1782 uint32_t *pdr_record_handle) 1783 { 1784 uint16_t header_length = 0; 1785 uint8_t num_children = 0; 1786 struct pldm_msgbuf _src; 1787 struct pldm_msgbuf *src = &_src; 1788 struct pldm_msgbuf _dst; 1789 struct pldm_msgbuf *dst = &_dst; 1790 int rc; 1791 pldm_pdr_record *record; 1792 pldm_pdr_record *prev; 1793 1794 if (!repo || !entity || !pdr_record_handle) { 1795 return -EINVAL; 1796 } 1797 record = repo->first; 1798 prev = repo->first; 1799 1800 rc = pldm_entity_association_find_record_handle_by_entity( 1801 repo, entity, is_remote, pdr_record_handle); 1802 if (rc) { 1803 return rc; 1804 } 1805 pldm_pdr_find_record_by_handle(&record, &prev, *pdr_record_handle); 1806 if (!record) { 1807 return -EINVAL; 1808 } 1809 // Initialize msg buffer for record and record->data 1810 rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1811 record->data, record->size); 1812 if (rc) { 1813 return rc; 1814 } 1815 // check if removing an entity from record causes overflow before 1816 // allocating memory for new_record. 1817 if (record->size < sizeof(pldm_entity)) { 1818 return -EOVERFLOW; 1819 } 1820 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record)); 1821 if (!new_record) { 1822 return -ENOMEM; 1823 } 1824 new_record->data = malloc(record->size - sizeof(pldm_entity)); 1825 if (!new_record->data) { 1826 rc = -ENOMEM; 1827 goto cleanup_new_record; 1828 } 1829 new_record->record_handle = record->record_handle; 1830 new_record->size = record->size - sizeof(struct pldm_entity); 1831 new_record->is_remote = record->is_remote; 1832 1833 // Initialize new PDR record with data from original PDR record. 1834 // Start with adding the header of original PDR 1835 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1836 new_record->data, new_record->size); 1837 if (rc) { 1838 goto cleanup_new_record_data; 1839 } 1840 pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle); 1841 pldm_msgbuf_copy(dst, src, uint8_t, hdr_version); 1842 pldm_msgbuf_copy(dst, src, uint8_t, hdr_type); 1843 pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num); 1844 // extract the header length from record and decrement size with 1845 // size of pldm_entity before inserting the value into new_record. 1846 rc = pldm_msgbuf_extract(src, header_length); 1847 if (rc) { 1848 goto cleanup_new_record_data; 1849 } 1850 if (header_length < sizeof(pldm_entity)) { 1851 rc = -EOVERFLOW; 1852 goto cleanup_new_record_data; 1853 } 1854 header_length -= sizeof(pldm_entity); 1855 pldm_msgbuf_insert(dst, header_length); 1856 pldm_msgbuf_copy(dst, src, uint16_t, container_id); 1857 pldm_msgbuf_copy(dst, src, uint8_t, association_type); 1858 pldm_msgbuf_copy(dst, src, uint16_t, entity_type); 1859 pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num); 1860 pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id); 1861 // extract value of number of children from record and decrement it 1862 // by 1 before insert the value to new record. 1863 rc = pldm_msgbuf_extract(src, num_children); 1864 if (rc) { 1865 goto cleanup_new_record_data; 1866 } 1867 if (num_children == 1) { 1868 prev->next = record->next; 1869 free(record->data); 1870 free(record); 1871 goto cleanup_new_record_data; 1872 } else if (num_children < 1) { 1873 rc = -EOVERFLOW; 1874 goto cleanup_new_record_data; 1875 } 1876 num_children -= 1; 1877 pldm_msgbuf_insert(dst, num_children); 1878 //Add all children of original PDR to new PDR 1879 for (int i = 0; i < num_children + 1; ++i) { 1880 struct pldm_entity e; 1881 1882 if ((rc = pldm_msgbuf_extract(src, e.entity_type)) || 1883 (rc = pldm_msgbuf_extract(src, e.entity_instance_num)) || 1884 (rc = pldm_msgbuf_extract(src, e.entity_container_id))) { 1885 goto cleanup_new_record_data; 1886 } 1887 1888 if (pldm_entity_cmp(entity, &e)) { 1889 continue; 1890 } 1891 1892 pldm_msgbuf_insert(dst, e.entity_type); 1893 pldm_msgbuf_insert(dst, e.entity_instance_num); 1894 pldm_msgbuf_insert(dst, e.entity_container_id); 1895 } 1896 1897 if ((rc = pldm_msgbuf_destroy(src)) || 1898 (rc = pldm_msgbuf_destroy(dst)) || 1899 (rc = pldm_pdr_replace_record(repo, record, prev, new_record))) { 1900 goto cleanup_new_record_data; 1901 } 1902 1903 free(record->data); 1904 free(record); 1905 return rc; 1906 1907 cleanup_new_record_data: 1908 free(new_record->data); 1909 cleanup_new_record: 1910 free(new_record); 1911 return rc; 1912 } 1913 1914 /* API to find the PDR record that is previous to a given PLDM PDR 1915 * record in a given PLDM PDR repository 1916 */ 1917 LIBPLDM_CC_NONNULL 1918 static pldm_pdr_record *pldm_pdr_get_prev_record(pldm_pdr *repo, 1919 pldm_pdr_record *record) 1920 { 1921 pldm_pdr_record *prev = NULL; 1922 pldm_pdr_record *curr = repo->first; 1923 1924 while (curr != NULL) { 1925 if (curr->record_handle == record->record_handle) { 1926 break; 1927 } 1928 prev = curr; 1929 curr = curr->next; 1930 } 1931 return prev; 1932 } 1933 1934 /* API to check if a PLDM PDR record is present in a PLDM PDR repository 1935 */ 1936 LIBPLDM_CC_NONNULL 1937 static bool is_prev_record_present(pldm_pdr *repo, pldm_pdr_record *record) 1938 { 1939 if (repo->first == record) { 1940 return true; 1941 } 1942 1943 return pldm_pdr_get_prev_record(repo, record) != NULL; 1944 } 1945 1946 /* API to check if FRU RSI of record matches the given record set identifier. 1947 * Returns 1 if the provided FRU record matches the provided record set identifier, 1948 * 0 if it does not, otherwise -EINVAL if the arguments are invalid. 1949 */ 1950 LIBPLDM_CC_NONNULL 1951 static int pldm_pdr_record_matches_fru_rsi(const pldm_pdr_record *record, 1952 uint16_t rsi) 1953 { 1954 uint16_t record_fru_rsi = 0; 1955 uint8_t *skip_data = NULL; 1956 uint8_t skip_data_size = 0; 1957 struct pldm_msgbuf _dst; 1958 struct pldm_msgbuf *dst = &_dst; 1959 int rc = 0; 1960 1961 rc = pldm_msgbuf_init_errno(dst, PDR_FRU_RECORD_SET_MIN_SIZE, 1962 record->data, record->size); 1963 if (rc) { 1964 return rc; 1965 } 1966 skip_data_size = sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t); 1967 pldm_msgbuf_span_required(dst, skip_data_size, (void **)&skip_data); 1968 pldm_msgbuf_extract(dst, record_fru_rsi); 1969 1970 rc = pldm_msgbuf_destroy(dst); 1971 if (rc) { 1972 return rc; 1973 } 1974 return record_fru_rsi == rsi; 1975 } 1976 1977 /* API to remove PLDM PDR record from a PLDM PDR repository 1978 */ 1979 LIBPLDM_CC_NONNULL_ARGS(1, 2) 1980 static int pldm_pdr_remove_record(pldm_pdr *repo, pldm_pdr_record *record, 1981 pldm_pdr_record *prev) 1982 { 1983 if (!is_prev_record_present(repo, record)) { 1984 return -EINVAL; 1985 } 1986 1987 assert(repo->size >= record->size); 1988 if (repo->size < record->size) { 1989 return -EOVERFLOW; 1990 } 1991 1992 if (repo->first == record) { 1993 repo->first = record->next; 1994 } else { 1995 if (prev != NULL) { 1996 prev->next = record->next; 1997 } 1998 } 1999 2000 if (repo->last == record) { 2001 repo->last = prev; 2002 if (prev != NULL) { 2003 prev->next = NULL; 2004 } 2005 } 2006 repo->record_count -= 1; 2007 repo->size -= record->size; 2008 free(record->data); 2009 free(record); 2010 2011 return 0; 2012 } 2013 2014 LIBPLDM_ABI_TESTING 2015 int pldm_pdr_remove_fru_record_set_by_rsi(pldm_pdr *repo, uint16_t fru_rsi, 2016 bool is_remote, 2017 uint32_t *record_handle) 2018 { 2019 pldm_pdr_record *record; 2020 pldm_pdr_record *prev = NULL; 2021 size_t skip_data_size = sizeof(uint32_t) + sizeof(uint8_t); 2022 uint8_t hdr_type = 0; 2023 int rc = 0; 2024 int match; 2025 2026 if (!repo || !record_handle) { 2027 return -EINVAL; 2028 } 2029 record = repo->first; 2030 2031 while (record != NULL) { 2032 struct pldm_msgbuf _buf; 2033 struct pldm_msgbuf *buf = &_buf; 2034 rc = pldm_msgbuf_init_errno(buf, PDR_FRU_RECORD_SET_MIN_SIZE, 2035 record->data, record->size); 2036 if (rc) { 2037 return rc; 2038 } 2039 pldm_msgbuf_span_required(buf, skip_data_size, NULL); 2040 if ((rc = pldm_msgbuf_extract(buf, hdr_type))) { 2041 return rc; 2042 } 2043 if (record->is_remote != is_remote || 2044 hdr_type != PLDM_PDR_FRU_RECORD_SET) { 2045 goto cleanup; 2046 } 2047 match = pldm_pdr_record_matches_fru_rsi(record, fru_rsi); 2048 if (match < 0) { 2049 return match; 2050 } 2051 if (match) { 2052 *record_handle = record->record_handle; 2053 prev = pldm_pdr_get_prev_record(repo, record); 2054 return pldm_pdr_remove_record(repo, record, prev); 2055 } 2056 cleanup: 2057 rc = pldm_msgbuf_destroy(buf); 2058 if (rc) { 2059 return rc; 2060 } 2061 record = record->next; 2062 } 2063 return rc; 2064 } 2065