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