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_STABLE 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_complete(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 PLDM_MSGBUF_DEFINE_P(src); 1557 PLDM_MSGBUF_DEFINE_P(dst); 1558 1559 pldm_pdr_find_record_by_handle(&record, &prev, pdr_record_handle); 1560 1561 if (!record) { 1562 return -EINVAL; 1563 } 1564 1565 // check if adding another entity to record causes overflow before 1566 // allocating memory for new_record. 1567 if (record->size + sizeof(pldm_entity) < sizeof(pldm_entity)) { 1568 return -EOVERFLOW; 1569 } 1570 1571 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record)); 1572 if (!new_record) { 1573 return -ENOMEM; 1574 } 1575 1576 new_record->data = malloc(record->size + sizeof(pldm_entity)); 1577 if (!new_record->data) { 1578 rc = -ENOMEM; 1579 goto cleanup_new_record; 1580 } 1581 1582 new_record->record_handle = record->record_handle; 1583 new_record->size = record->size + sizeof(struct pldm_entity); 1584 new_record->is_remote = record->is_remote; 1585 1586 // Initialize msg buffer for record and record->data 1587 rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1588 record->data, record->size); 1589 if (rc) { 1590 goto cleanup_new_record_data; 1591 } 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_src_msgbuf; 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_dst_msgbuf; 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_dst_msgbuf; 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_dst_msgbuf; 1629 } 1630 if (num_children == UINT8_MAX) { 1631 rc = -EOVERFLOW; 1632 goto cleanup_dst_msgbuf; 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_complete(src); 1645 if (rc) { 1646 rc = pldm_msgbuf_discard(dst, rc); 1647 goto cleanup_new_record_data; 1648 } 1649 rc = pldm_msgbuf_init_errno(src, sizeof(struct pldm_entity), entity, 1650 sizeof(struct pldm_entity)); 1651 if (rc) { 1652 rc = pldm_msgbuf_discard(dst, rc); 1653 goto cleanup_new_record_data; 1654 } 1655 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_type); 1656 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_instance_num); 1657 pldm_msgbuf_copy(dst, src, uint16_t, child_entity_container_id); 1658 1659 rc = pldm_msgbuf_complete(dst); 1660 if (rc) { 1661 goto cleanup_src_msgbuf; 1662 } 1663 rc = pldm_msgbuf_complete(src); 1664 if (rc) { 1665 goto cleanup_new_record_data; 1666 } 1667 1668 rc = pldm_pdr_replace_record(repo, record, prev, new_record); 1669 if (rc) { 1670 goto cleanup_new_record_data; 1671 } 1672 1673 free(record->data); 1674 free(record); 1675 return rc; 1676 cleanup_dst_msgbuf: 1677 rc = pldm_msgbuf_discard(dst, rc); 1678 cleanup_src_msgbuf: 1679 rc = pldm_msgbuf_discard(src, rc); 1680 cleanup_new_record_data: 1681 free(new_record->data); 1682 cleanup_new_record: 1683 free(new_record); 1684 return rc; 1685 } 1686 1687 LIBPLDM_ABI_TESTING 1688 int pldm_entity_association_pdr_create_new(pldm_pdr *repo, 1689 uint32_t pdr_record_handle, 1690 pldm_entity *parent, 1691 pldm_entity *entity, 1692 uint32_t *entity_record_handle) 1693 { 1694 if (!repo || !parent || !entity || !entity_record_handle) { 1695 return -EINVAL; 1696 } 1697 1698 if (pdr_record_handle == UINT32_MAX) { 1699 return -EOVERFLOW; 1700 } 1701 1702 bool pdr_added = false; 1703 uint16_t new_pdr_size; 1704 uint16_t container_id = 0; 1705 void *container_id_addr; 1706 PLDM_MSGBUF_DEFINE_P(dst); 1707 PLDM_MSGBUF_DEFINE_P(src_p); 1708 PLDM_MSGBUF_DEFINE_P(src_c); 1709 int rc = 0; 1710 1711 pldm_pdr_record *prev = repo->first; 1712 pldm_pdr_record *record = repo->first; 1713 pdr_added = pldm_pdr_find_record_by_handle(&record, &prev, 1714 pdr_record_handle); 1715 if (!pdr_added) { 1716 return -ENOENT; 1717 } 1718 1719 static_assert(PDR_ENTITY_ASSOCIATION_MIN_SIZE < UINT16_MAX, 1720 "Truncation ahead"); 1721 new_pdr_size = PDR_ENTITY_ASSOCIATION_MIN_SIZE; 1722 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record)); 1723 if (!new_record) { 1724 return -ENOMEM; 1725 } 1726 1727 new_record->data = malloc(new_pdr_size); 1728 if (!new_record->data) { 1729 rc = -ENOMEM; 1730 goto cleanup_new_record; 1731 } 1732 1733 // Initialise new PDR to be added with the header, size and handle. 1734 // Set the position of new PDR 1735 *entity_record_handle = pdr_record_handle + 1; 1736 new_record->record_handle = *entity_record_handle; 1737 new_record->size = new_pdr_size; 1738 new_record->is_remote = false; 1739 1740 rc = pldm_msgbuf_init_errno(dst, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1741 new_record->data, new_record->size); 1742 if (rc) { 1743 goto cleanup_new_record_data; 1744 } 1745 1746 // header record handle 1747 pldm_msgbuf_insert(dst, *entity_record_handle); 1748 // header version 1749 pldm_msgbuf_insert_uint8(dst, 1); 1750 // header type 1751 pldm_msgbuf_insert_uint8(dst, PLDM_PDR_ENTITY_ASSOCIATION); 1752 // header change number 1753 pldm_msgbuf_insert_uint16(dst, 0); 1754 // header length 1755 pldm_msgbuf_insert_uint16(dst, 1756 (new_pdr_size - sizeof(struct pldm_pdr_hdr))); 1757 1758 // Data for new PDR is obtained from parent PDR and new contained entity 1759 // is added as the child 1760 rc = pldm_msgbuf_init_errno(src_p, sizeof(struct pldm_entity), parent, 1761 sizeof(*parent)); 1762 if (rc) { 1763 goto cleanup_msgbuf_dst; 1764 } 1765 1766 rc = pldm_msgbuf_init_errno(src_c, sizeof(struct pldm_entity), entity, 1767 sizeof(*entity)); 1768 if (rc) { 1769 goto cleanup_msgbuf_src_p; 1770 } 1771 1772 container_id_addr = NULL; 1773 // extract pointer for container ID and save the address 1774 rc = pldm_msgbuf_span_required(dst, sizeof(container_id), 1775 (void **)&container_id_addr); 1776 if (rc) { 1777 goto cleanup_msgbuf_src_c; 1778 } 1779 assert(container_id_addr); 1780 pldm_msgbuf_insert_uint8(dst, PLDM_ENTITY_ASSOCIAION_PHYSICAL); 1781 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_type); 1782 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_instance_num); 1783 pldm_msgbuf_copy(dst, src_p, uint16_t, entity_container_id); 1784 // number of children 1785 pldm_msgbuf_insert_uint8(dst, 1); 1786 1787 // Add new entity as child 1788 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_type); 1789 pldm_msgbuf_copy(dst, src_c, uint16_t, child_entity_instance_num); 1790 // Extract and insert child entity container ID and add same value to 1791 // container ID of entity 1792 pldm_msgbuf_extract(src_c, container_id); 1793 pldm_msgbuf_insert(dst, container_id); 1794 container_id = htole16(container_id); 1795 memcpy(container_id_addr, &container_id, sizeof(uint16_t)); 1796 1797 rc = pldm_msgbuf_complete(src_c); 1798 if (rc) { 1799 goto cleanup_msgbuf_src_p; 1800 } 1801 rc = pldm_msgbuf_complete(src_p); 1802 if (rc) { 1803 goto cleanup_msgbuf_dst; 1804 } 1805 rc = pldm_msgbuf_complete(dst); 1806 if (rc) { 1807 goto cleanup_new_record_data; 1808 } 1809 1810 rc = pldm_pdr_insert_record(repo, record, new_record); 1811 if (rc) { 1812 goto cleanup_new_record_data; 1813 } 1814 1815 return rc; 1816 cleanup_msgbuf_src_c: 1817 rc = pldm_msgbuf_discard(src_c, rc); 1818 cleanup_msgbuf_src_p: 1819 rc = pldm_msgbuf_discard(src_p, rc); 1820 cleanup_msgbuf_dst: 1821 rc = pldm_msgbuf_discard(dst, rc); 1822 cleanup_new_record_data: 1823 free(new_record->data); 1824 cleanup_new_record: 1825 free(new_record); 1826 return rc; 1827 } 1828 1829 LIBPLDM_CC_NONNULL 1830 static bool pldm_entity_cmp(const struct pldm_entity *l, 1831 const struct pldm_entity *r) 1832 { 1833 return l->entity_type == r->entity_type && 1834 l->entity_instance_num == r->entity_instance_num && 1835 l->entity_container_id == r->entity_container_id; 1836 } 1837 1838 /* Find record handle of a PDR record from PDR repo and 1839 * entity 1840 */ 1841 LIBPLDM_CC_NONNULL 1842 static int pldm_entity_association_find_record_handle_by_entity( 1843 pldm_pdr *repo, pldm_entity *entity, bool is_remote, 1844 uint32_t *record_handle) 1845 { 1846 uint8_t num_children = 0; 1847 uint8_t hdr_type = 0; 1848 int rc = 0; 1849 size_t skip_data_size = 0; 1850 pldm_pdr_record *record = repo->first; 1851 1852 while (record != NULL) { 1853 PLDM_MSGBUF_DEFINE_P(dst); 1854 1855 rc = pldm_msgbuf_init_errno(dst, 1856 PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1857 record->data, record->size); 1858 if (rc) { 1859 return rc; 1860 } 1861 skip_data_size = sizeof(uint32_t) + sizeof(uint8_t); 1862 pldm_msgbuf_span_required(dst, skip_data_size, NULL); 1863 rc = pldm_msgbuf_extract(dst, hdr_type); 1864 if (rc) { 1865 return pldm_msgbuf_discard(dst, rc); 1866 } 1867 if (record->is_remote != is_remote || 1868 hdr_type != PLDM_PDR_ENTITY_ASSOCIATION) { 1869 goto cleanup; 1870 } 1871 skip_data_size = sizeof(uint16_t) + sizeof(uint16_t) + 1872 sizeof(uint16_t) + sizeof(uint8_t) + 1873 sizeof(struct pldm_entity); 1874 pldm_msgbuf_span_required(dst, skip_data_size, NULL); 1875 rc = pldm_msgbuf_extract(dst, num_children); 1876 if (rc) { 1877 return pldm_msgbuf_discard(dst, rc); 1878 } 1879 for (int i = 0; i < num_children; ++i) { 1880 struct pldm_entity e; 1881 1882 if ((rc = pldm_msgbuf_extract(dst, e.entity_type)) || 1883 (rc = pldm_msgbuf_extract(dst, 1884 e.entity_instance_num)) || 1885 (rc = pldm_msgbuf_extract(dst, 1886 e.entity_container_id))) { 1887 return pldm_msgbuf_discard(dst, rc); 1888 } 1889 1890 if (pldm_entity_cmp(entity, &e)) { 1891 *record_handle = record->record_handle; 1892 return pldm_msgbuf_complete(dst); 1893 } 1894 } 1895 cleanup: 1896 rc = pldm_msgbuf_complete(dst); 1897 if (rc) { 1898 return rc; 1899 } 1900 record = record->next; 1901 } 1902 return 0; 1903 } 1904 1905 LIBPLDM_ABI_TESTING 1906 int pldm_entity_association_pdr_remove_contained_entity( 1907 pldm_pdr *repo, pldm_entity *entity, bool is_remote, 1908 uint32_t *pdr_record_handle) 1909 { 1910 uint16_t header_length = 0; 1911 uint8_t num_children = 0; 1912 PLDM_MSGBUF_DEFINE_P(src); 1913 PLDM_MSGBUF_DEFINE_P(dst); 1914 int rc; 1915 pldm_pdr_record *record; 1916 pldm_pdr_record *prev; 1917 1918 if (!repo || !entity || !pdr_record_handle) { 1919 return -EINVAL; 1920 } 1921 record = repo->first; 1922 prev = repo->first; 1923 1924 rc = pldm_entity_association_find_record_handle_by_entity( 1925 repo, entity, is_remote, pdr_record_handle); 1926 if (rc) { 1927 return rc; 1928 } 1929 pldm_pdr_find_record_by_handle(&record, &prev, *pdr_record_handle); 1930 if (!record) { 1931 return -EINVAL; 1932 } 1933 // check if removing an entity from record causes overflow before 1934 // allocating memory for new_record. 1935 if (record->size < sizeof(pldm_entity)) { 1936 return -EOVERFLOW; 1937 } 1938 pldm_pdr_record *new_record = malloc(sizeof(pldm_pdr_record)); 1939 if (!new_record) { 1940 return -ENOMEM; 1941 } 1942 new_record->data = malloc(record->size - sizeof(pldm_entity)); 1943 if (!new_record->data) { 1944 rc = -ENOMEM; 1945 goto cleanup_new_record; 1946 } 1947 new_record->record_handle = record->record_handle; 1948 new_record->size = record->size - sizeof(struct pldm_entity); 1949 new_record->is_remote = record->is_remote; 1950 1951 // Initialize msg buffer for record and record->data 1952 rc = pldm_msgbuf_init_errno(src, PDR_ENTITY_ASSOCIATION_MIN_SIZE, 1953 record->data, record->size); 1954 if (rc) { 1955 goto cleanup_new_record_data; 1956 } 1957 1958 // Initialize new PDR record with data from original PDR record. 1959 // Start with adding the header of original PDR 1960 rc = pldm_msgbuf_init_errno( 1961 dst, (PDR_ENTITY_ASSOCIATION_MIN_SIZE - sizeof(pldm_entity)), 1962 new_record->data, new_record->size); 1963 if (rc) { 1964 goto cleanup_msgbuf_src; 1965 } 1966 pldm_msgbuf_copy(dst, src, uint32_t, hdr_record_handle); 1967 pldm_msgbuf_copy(dst, src, uint8_t, hdr_version); 1968 pldm_msgbuf_copy(dst, src, uint8_t, hdr_type); 1969 pldm_msgbuf_copy(dst, src, uint16_t, hdr_record_change_num); 1970 // extract the header length from record and decrement size with 1971 // size of pldm_entity before inserting the value into new_record. 1972 rc = pldm_msgbuf_extract(src, header_length); 1973 if (rc) { 1974 goto cleanup_msgbuf_dst; 1975 } 1976 if (header_length < sizeof(pldm_entity)) { 1977 rc = -EOVERFLOW; 1978 goto cleanup_msgbuf_dst; 1979 } 1980 header_length -= sizeof(pldm_entity); 1981 pldm_msgbuf_insert(dst, header_length); 1982 pldm_msgbuf_copy(dst, src, uint16_t, container_id); 1983 pldm_msgbuf_copy(dst, src, uint8_t, association_type); 1984 pldm_msgbuf_copy(dst, src, uint16_t, entity_type); 1985 pldm_msgbuf_copy(dst, src, uint16_t, entity_instance_num); 1986 pldm_msgbuf_copy(dst, src, uint16_t, entity_container_id); 1987 // extract value of number of children from record and decrement it 1988 // by 1 before insert the value to new record. 1989 rc = pldm_msgbuf_extract(src, num_children); 1990 if (rc) { 1991 goto cleanup_msgbuf_dst; 1992 } 1993 if (num_children == 1) { 1994 // This is the last child which is getting removed so we need to delete the Entity Association PDR. 1995 pldm_pdr_remove_record(repo, record, 1996 pldm_pdr_get_prev_record(repo, record)); 1997 goto cleanup_msgbuf_dst; 1998 } else if (num_children < 1) { 1999 rc = -EOVERFLOW; 2000 goto cleanup_msgbuf_dst; 2001 } 2002 num_children -= 1; 2003 pldm_msgbuf_insert(dst, num_children); 2004 //Add all children of original PDR to new PDR 2005 for (int i = 0; i < num_children + 1; ++i) { 2006 struct pldm_entity e; 2007 2008 if ((rc = pldm_msgbuf_extract(src, e.entity_type)) || 2009 (rc = pldm_msgbuf_extract(src, e.entity_instance_num)) || 2010 (rc = pldm_msgbuf_extract(src, e.entity_container_id))) { 2011 goto cleanup_msgbuf_dst; 2012 } 2013 2014 if (pldm_entity_cmp(entity, &e)) { 2015 continue; 2016 } 2017 2018 pldm_msgbuf_insert(dst, e.entity_type); 2019 pldm_msgbuf_insert(dst, e.entity_instance_num); 2020 pldm_msgbuf_insert(dst, e.entity_container_id); 2021 } 2022 2023 rc = pldm_msgbuf_complete(dst); 2024 if (rc) { 2025 goto cleanup_msgbuf_src; 2026 } 2027 2028 rc = pldm_msgbuf_complete(src); 2029 if (rc) { 2030 goto cleanup_new_record_data; 2031 } 2032 2033 rc = pldm_pdr_replace_record(repo, record, prev, new_record); 2034 if (rc) { 2035 goto cleanup_new_record_data; 2036 } 2037 2038 free(record->data); 2039 free(record); 2040 return rc; 2041 2042 cleanup_msgbuf_dst: 2043 rc = pldm_msgbuf_discard(dst, rc); 2044 cleanup_msgbuf_src: 2045 rc = pldm_msgbuf_discard(src, rc); 2046 cleanup_new_record_data: 2047 free(new_record->data); 2048 cleanup_new_record: 2049 free(new_record); 2050 return rc; 2051 } 2052 2053 /* API to find the PDR record that is previous to a given PLDM PDR 2054 * record in a given PLDM PDR repository 2055 */ 2056 LIBPLDM_CC_NONNULL 2057 static pldm_pdr_record *pldm_pdr_get_prev_record(pldm_pdr *repo, 2058 pldm_pdr_record *record) 2059 { 2060 pldm_pdr_record *prev = NULL; 2061 pldm_pdr_record *curr = repo->first; 2062 2063 while (curr != NULL) { 2064 if (curr->record_handle == record->record_handle) { 2065 break; 2066 } 2067 prev = curr; 2068 curr = curr->next; 2069 } 2070 return prev; 2071 } 2072 2073 /* API to check if a PLDM PDR record is present in a PLDM PDR repository 2074 */ 2075 LIBPLDM_CC_NONNULL 2076 static bool is_prev_record_present(pldm_pdr *repo, pldm_pdr_record *record) 2077 { 2078 if (repo->first == record) { 2079 return true; 2080 } 2081 2082 return pldm_pdr_get_prev_record(repo, record) != NULL; 2083 } 2084 2085 /* API to check if FRU RSI of record matches the given record set identifier. 2086 * Returns 1 if the provided FRU record matches the provided record set identifier, 2087 * 0 if it does not, otherwise -EINVAL if the arguments are invalid. 2088 */ 2089 LIBPLDM_CC_NONNULL 2090 static int pldm_pdr_record_matches_fru_rsi(const pldm_pdr_record *record, 2091 uint16_t rsi) 2092 { 2093 uint16_t record_fru_rsi = 0; 2094 uint8_t *skip_data = NULL; 2095 uint8_t skip_data_size = 0; 2096 PLDM_MSGBUF_DEFINE_P(dst); 2097 int rc = 0; 2098 2099 rc = pldm_msgbuf_init_errno(dst, PDR_FRU_RECORD_SET_MIN_SIZE, 2100 record->data, record->size); 2101 if (rc) { 2102 return rc; 2103 } 2104 skip_data_size = sizeof(struct pldm_pdr_hdr) + sizeof(uint16_t); 2105 pldm_msgbuf_span_required(dst, skip_data_size, (void **)&skip_data); 2106 pldm_msgbuf_extract(dst, record_fru_rsi); 2107 2108 rc = pldm_msgbuf_complete(dst); 2109 if (rc) { 2110 return rc; 2111 } 2112 return record_fru_rsi == rsi; 2113 } 2114 2115 /* API to remove PLDM PDR record from a PLDM PDR repository 2116 */ 2117 LIBPLDM_CC_NONNULL_ARGS(1, 2) 2118 static int pldm_pdr_remove_record(pldm_pdr *repo, pldm_pdr_record *record, 2119 pldm_pdr_record *prev) 2120 { 2121 if (!is_prev_record_present(repo, record)) { 2122 return -EINVAL; 2123 } 2124 2125 assert(repo->size >= record->size); 2126 if (repo->size < record->size) { 2127 return -EOVERFLOW; 2128 } 2129 2130 if (repo->first == record) { 2131 repo->first = record->next; 2132 } else { 2133 if (prev != NULL) { 2134 prev->next = record->next; 2135 } 2136 } 2137 2138 if (repo->last == record) { 2139 repo->last = prev; 2140 if (prev != NULL) { 2141 prev->next = NULL; 2142 } 2143 } 2144 repo->record_count -= 1; 2145 repo->size -= record->size; 2146 free(record->data); 2147 free(record); 2148 2149 return 0; 2150 } 2151 2152 LIBPLDM_ABI_TESTING 2153 int pldm_pdr_remove_fru_record_set_by_rsi(pldm_pdr *repo, uint16_t fru_rsi, 2154 bool is_remote, 2155 uint32_t *record_handle) 2156 { 2157 pldm_pdr_record *record; 2158 pldm_pdr_record *prev = NULL; 2159 size_t skip_data_size = sizeof(uint32_t) + sizeof(uint8_t); 2160 uint8_t hdr_type = 0; 2161 int rc = 0; 2162 int match; 2163 2164 if (!repo || !record_handle) { 2165 return -EINVAL; 2166 } 2167 record = repo->first; 2168 2169 while (record != NULL) { 2170 PLDM_MSGBUF_DEFINE_P(buf); 2171 2172 rc = pldm_msgbuf_init_errno(buf, PDR_FRU_RECORD_SET_MIN_SIZE, 2173 record->data, record->size); 2174 if (rc) { 2175 return rc; 2176 } 2177 pldm_msgbuf_span_required(buf, skip_data_size, NULL); 2178 pldm_msgbuf_extract(buf, hdr_type); 2179 rc = pldm_msgbuf_complete(buf); 2180 if (rc) { 2181 return rc; 2182 } 2183 if (record->is_remote != is_remote || 2184 hdr_type != PLDM_PDR_FRU_RECORD_SET) { 2185 goto next; 2186 } 2187 match = pldm_pdr_record_matches_fru_rsi(record, fru_rsi); 2188 if (match < 0) { 2189 return match; 2190 } 2191 if (match) { 2192 *record_handle = record->record_handle; 2193 prev = pldm_pdr_get_prev_record(repo, record); 2194 return pldm_pdr_remove_record(repo, record, prev); 2195 } 2196 next: 2197 record = record->next; 2198 } 2199 return rc; 2200 } 2201