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