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