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