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