1 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ 2 #include <libpldm/base.h> 3 #include <libpldm/fru.h> 4 #include <libpldm/utils.h> 5 6 #include <assert.h> 7 #include <endian.h> 8 #include <stdbool.h> 9 #include <stdint.h> 10 #include <string.h> 11 12 LIBPLDM_ABI_STABLE 13 int encode_get_fru_record_table_metadata_req(uint8_t instance_id, 14 struct pldm_msg *msg, 15 size_t payload_length) 16 { 17 if (msg == NULL) { 18 return PLDM_ERROR_INVALID_DATA; 19 } 20 21 if (payload_length != PLDM_GET_FRU_RECORD_TABLE_METADATA_REQ_BYTES) { 22 return PLDM_ERROR_INVALID_LENGTH; 23 } 24 25 struct pldm_header_info header = { 0 }; 26 header.instance = instance_id; 27 header.msg_type = PLDM_REQUEST; 28 header.pldm_type = PLDM_FRU; 29 header.command = PLDM_GET_FRU_RECORD_TABLE_METADATA; 30 31 return pack_pldm_header(&header, &(msg->hdr)); 32 } 33 34 LIBPLDM_ABI_STABLE 35 int decode_get_fru_record_table_metadata_resp( 36 const struct pldm_msg *msg, size_t payload_length, 37 uint8_t *completion_code, uint8_t *fru_data_major_version, 38 uint8_t *fru_data_minor_version, uint32_t *fru_table_maximum_size, 39 uint32_t *fru_table_length, uint16_t *total_record_set_identifiers, 40 uint16_t *total_table_records, uint32_t *checksum) 41 { 42 if (msg == NULL || completion_code == NULL || 43 fru_data_major_version == NULL || fru_data_minor_version == NULL || 44 fru_table_maximum_size == NULL || fru_table_length == NULL || 45 total_record_set_identifiers == NULL || 46 total_table_records == NULL || checksum == NULL) { 47 return PLDM_ERROR_INVALID_DATA; 48 } 49 50 *completion_code = msg->payload[0]; 51 if (PLDM_SUCCESS != *completion_code) { 52 return PLDM_SUCCESS; 53 } 54 55 if (payload_length != PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES) { 56 return PLDM_ERROR_INVALID_LENGTH; 57 } 58 59 struct pldm_get_fru_record_table_metadata_resp *response = 60 (struct pldm_get_fru_record_table_metadata_resp *)msg->payload; 61 62 *fru_data_major_version = response->fru_data_major_version; 63 *fru_data_minor_version = response->fru_data_minor_version; 64 *fru_table_maximum_size = le32toh(response->fru_table_maximum_size); 65 *fru_table_length = le32toh(response->fru_table_length); 66 *total_record_set_identifiers = 67 le16toh(response->total_record_set_identifiers); 68 *total_table_records = le16toh(response->total_table_records); 69 *checksum = le32toh(response->checksum); 70 71 return PLDM_SUCCESS; 72 } 73 74 LIBPLDM_ABI_STABLE 75 int encode_get_fru_record_table_metadata_resp( 76 uint8_t instance_id, uint8_t completion_code, 77 uint8_t fru_data_major_version, uint8_t fru_data_minor_version, 78 uint32_t fru_table_maximum_size, uint32_t fru_table_length, 79 uint16_t total_record_set_identifiers, uint16_t total_table_records, 80 uint32_t checksum, struct pldm_msg *msg) 81 { 82 if (msg == NULL) { 83 return PLDM_ERROR_INVALID_DATA; 84 } 85 86 struct pldm_header_info header = { 0 }; 87 header.msg_type = PLDM_RESPONSE; 88 header.instance = instance_id; 89 header.pldm_type = PLDM_FRU; 90 header.command = PLDM_GET_FRU_RECORD_TABLE_METADATA; 91 92 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 93 if (PLDM_SUCCESS != rc) { 94 return rc; 95 } 96 97 struct pldm_get_fru_record_table_metadata_resp *response = 98 (struct pldm_get_fru_record_table_metadata_resp *)msg->payload; 99 response->completion_code = completion_code; 100 if (response->completion_code == PLDM_SUCCESS) { 101 response->fru_data_major_version = fru_data_major_version; 102 response->fru_data_minor_version = fru_data_minor_version; 103 response->fru_table_maximum_size = 104 htole32(fru_table_maximum_size); 105 response->fru_table_length = htole32(fru_table_length); 106 response->total_record_set_identifiers = 107 htole16(total_record_set_identifiers); 108 response->total_table_records = htole16(total_table_records); 109 response->checksum = htole32(checksum); 110 } 111 112 return PLDM_SUCCESS; 113 } 114 115 LIBPLDM_ABI_STABLE 116 int decode_get_fru_record_table_req(const struct pldm_msg *msg, 117 size_t payload_length, 118 uint32_t *data_transfer_handle, 119 uint8_t *transfer_operation_flag) 120 { 121 if (msg == NULL || data_transfer_handle == NULL || 122 transfer_operation_flag == NULL) { 123 return PLDM_ERROR_INVALID_DATA; 124 } 125 126 if (payload_length != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES) { 127 return PLDM_ERROR_INVALID_LENGTH; 128 } 129 130 struct pldm_get_fru_record_table_req *req = 131 (struct pldm_get_fru_record_table_req *)msg->payload; 132 133 *data_transfer_handle = le32toh(req->data_transfer_handle); 134 *transfer_operation_flag = req->transfer_operation_flag; 135 136 return PLDM_SUCCESS; 137 } 138 139 LIBPLDM_ABI_STABLE 140 int encode_get_fru_record_table_resp(uint8_t instance_id, 141 uint8_t completion_code, 142 uint32_t next_data_transfer_handle, 143 uint8_t transfer_flag, 144 struct pldm_msg *msg) 145 { 146 if (msg == NULL) { 147 return PLDM_ERROR_INVALID_DATA; 148 } 149 150 struct pldm_header_info header = { 0 }; 151 header.msg_type = PLDM_RESPONSE; 152 header.instance = instance_id; 153 header.pldm_type = PLDM_FRU; 154 header.command = PLDM_GET_FRU_RECORD_TABLE; 155 156 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 157 if (rc > PLDM_SUCCESS) { 158 return rc; 159 } 160 161 struct pldm_get_fru_record_table_resp *resp = 162 (struct pldm_get_fru_record_table_resp *)msg->payload; 163 164 resp->completion_code = completion_code; 165 166 if (resp->completion_code == PLDM_SUCCESS) { 167 resp->next_data_transfer_handle = 168 htole32(next_data_transfer_handle); 169 resp->transfer_flag = transfer_flag; 170 } 171 172 return PLDM_SUCCESS; 173 } 174 175 LIBPLDM_ABI_DEPRECATED_UNSAFE 176 int encode_fru_record(uint8_t *fru_table, size_t total_size, size_t *curr_size, 177 uint16_t record_set_id, uint8_t record_type, 178 uint8_t num_frus, uint8_t encoding, uint8_t *tlvs, 179 size_t tlvs_size) 180 { 181 size_t record_hdr_size = sizeof(struct pldm_fru_record_data_format) - 182 sizeof(struct pldm_fru_record_tlv); 183 184 if (fru_table == NULL || curr_size == NULL || !tlvs_size) { 185 return PLDM_ERROR_INVALID_DATA; 186 } 187 188 if (SIZE_MAX - *curr_size < record_hdr_size) { 189 return PLDM_ERROR_INVALID_LENGTH; 190 } 191 192 if (SIZE_MAX - (*curr_size + record_hdr_size) < tlvs_size) { 193 return PLDM_ERROR_INVALID_LENGTH; 194 } 195 196 if (total_size < *curr_size + record_hdr_size) { 197 return PLDM_ERROR_INVALID_LENGTH; 198 } 199 200 if (total_size - (*curr_size + record_hdr_size) < tlvs_size) { 201 return PLDM_ERROR_INVALID_LENGTH; 202 } 203 204 struct pldm_fru_record_data_format *record = 205 (struct pldm_fru_record_data_format *)(fru_table + *curr_size); 206 record->record_set_id = htole16(record_set_id); 207 record->record_type = record_type; 208 record->num_fru_fields = num_frus; 209 record->encoding_type = encoding; 210 *curr_size += record_hdr_size; 211 212 if (tlvs) { 213 memcpy(fru_table + *curr_size, tlvs, tlvs_size); 214 *curr_size += tlvs_size; 215 } 216 217 return PLDM_SUCCESS; 218 } 219 220 static bool is_table_end(const struct pldm_fru_record_data_format *p, 221 const void *table, size_t table_size) 222 { 223 return p >= 224 (const struct pldm_fru_record_data_format *)((uint8_t *)table + 225 table_size); 226 } 227 228 LIBPLDM_ABI_STABLE 229 int get_fru_record_by_option(const uint8_t *table, size_t table_size, 230 uint8_t *record_table, size_t *record_size, 231 uint16_t rsi, uint8_t rt, uint8_t ft) 232 { 233 const struct pldm_fru_record_data_format *record_data_src = 234 (const struct pldm_fru_record_data_format *)table; 235 struct pldm_fru_record_data_format *record_data_dest; 236 int count = 0; 237 238 const struct pldm_fru_record_tlv *tlv; 239 size_t len; 240 uint8_t *pos = record_table; 241 242 while (!is_table_end(record_data_src, table, table_size)) { 243 if ((record_data_src->record_set_id != htole16(rsi) && 244 rsi != 0) || 245 (record_data_src->record_type != rt && rt != 0)) { 246 tlv = record_data_src->tlvs; 247 for (int i = 0; i < record_data_src->num_fru_fields; 248 i++) { 249 len = sizeof(*tlv) - 1 + tlv->length; 250 tlv = (const struct pldm_fru_record_tlv 251 *)((char *)tlv + len); 252 } 253 record_data_src = 254 (const struct pldm_fru_record_data_format 255 *)(tlv); 256 continue; 257 } 258 259 len = sizeof(struct pldm_fru_record_data_format) - 260 sizeof(struct pldm_fru_record_tlv); 261 262 if (pos - record_table + len >= *record_size) { 263 return PLDM_ERROR_INVALID_LENGTH; 264 } 265 memcpy(pos, record_data_src, len); 266 267 record_data_dest = (struct pldm_fru_record_data_format *)pos; 268 pos += len; 269 270 tlv = record_data_src->tlvs; 271 count = 0; 272 for (int i = 0; i < record_data_src->num_fru_fields; i++) { 273 len = sizeof(*tlv) - 1 + tlv->length; 274 if (tlv->type == ft || ft == 0) { 275 if (pos - record_table + len >= *record_size) { 276 return PLDM_ERROR_INVALID_LENGTH; 277 } 278 memcpy(pos, tlv, len); 279 pos += len; 280 count++; 281 } 282 tlv = (const struct pldm_fru_record_tlv *)((char *)tlv + 283 len); 284 } 285 record_data_dest->num_fru_fields = count; 286 record_data_src = 287 (const struct pldm_fru_record_data_format *)(tlv); 288 } 289 290 *record_size = pos - record_table; 291 292 return PLDM_SUCCESS; 293 } 294 295 LIBPLDM_ABI_STABLE 296 int encode_get_fru_record_by_option_req( 297 uint8_t instance_id, uint32_t data_transfer_handle, 298 uint16_t fru_table_handle, uint16_t record_set_identifier, 299 uint8_t record_type, uint8_t field_type, uint8_t transfer_op_flag, 300 struct pldm_msg *msg, size_t payload_length) 301 { 302 if (msg == NULL) { 303 return PLDM_ERROR_INVALID_DATA; 304 } 305 306 if (payload_length != 307 sizeof(struct pldm_get_fru_record_by_option_req)) { 308 return PLDM_ERROR_INVALID_LENGTH; 309 } 310 311 struct pldm_header_info header = { 0 }; 312 header.instance = instance_id; 313 header.msg_type = PLDM_REQUEST; 314 header.pldm_type = PLDM_FRU; 315 header.command = PLDM_GET_FRU_RECORD_BY_OPTION; 316 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 317 if (rc != PLDM_SUCCESS) { 318 return rc; 319 } 320 321 struct pldm_get_fru_record_by_option_req *req = 322 (struct pldm_get_fru_record_by_option_req *)msg->payload; 323 324 req->data_transfer_handle = htole32(data_transfer_handle); 325 req->fru_table_handle = htole16(fru_table_handle); 326 req->record_set_identifier = htole16(record_set_identifier); 327 req->record_type = record_type; 328 req->field_type = field_type; 329 req->transfer_op_flag = transfer_op_flag; 330 331 return PLDM_SUCCESS; 332 } 333 334 LIBPLDM_ABI_STABLE 335 int decode_get_fru_record_by_option_req( 336 const struct pldm_msg *msg, size_t payload_length, 337 uint32_t *data_transfer_handle, uint16_t *fru_table_handle, 338 uint16_t *record_set_identifier, uint8_t *record_type, 339 uint8_t *field_type, uint8_t *transfer_op_flag) 340 { 341 if (msg == NULL || data_transfer_handle == NULL || 342 fru_table_handle == NULL || record_set_identifier == NULL || 343 record_type == NULL || field_type == NULL || 344 transfer_op_flag == NULL) { 345 return PLDM_ERROR_INVALID_DATA; 346 } 347 348 if (payload_length != 349 sizeof(struct pldm_get_fru_record_by_option_req)) { 350 return PLDM_ERROR_INVALID_LENGTH; 351 } 352 353 struct pldm_get_fru_record_by_option_req *req = 354 (struct pldm_get_fru_record_by_option_req *)msg->payload; 355 356 *data_transfer_handle = le32toh(req->data_transfer_handle); 357 *fru_table_handle = le16toh(req->fru_table_handle); 358 *record_set_identifier = le16toh(req->record_set_identifier); 359 *record_type = req->record_type; 360 *field_type = req->field_type; 361 *transfer_op_flag = req->transfer_op_flag; 362 return PLDM_SUCCESS; 363 } 364 365 LIBPLDM_ABI_STABLE 366 int encode_get_fru_record_by_option_resp(uint8_t instance_id, 367 uint8_t completion_code, 368 uint32_t next_data_transfer_handle, 369 uint8_t transfer_flag, 370 const void *fru_structure_data, 371 size_t data_size, struct pldm_msg *msg, 372 size_t payload_length) 373 { 374 if (msg == NULL || fru_structure_data == NULL) { 375 return PLDM_ERROR_INVALID_DATA; 376 } 377 378 if (payload_length < PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES) { 379 return PLDM_ERROR_INVALID_LENGTH; 380 } 381 382 if (payload_length - PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES < 383 data_size) { 384 return PLDM_ERROR_INVALID_LENGTH; 385 } 386 387 struct pldm_header_info header = { 0 }; 388 header.instance = instance_id; 389 header.msg_type = PLDM_RESPONSE; 390 header.pldm_type = PLDM_FRU; 391 header.command = PLDM_GET_FRU_RECORD_BY_OPTION; 392 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 393 if (rc != PLDM_SUCCESS) { 394 return rc; 395 } 396 397 struct pldm_get_fru_record_by_option_resp *resp = 398 (struct pldm_get_fru_record_by_option_resp *)msg->payload; 399 400 resp->completion_code = completion_code; 401 resp->next_data_transfer_handle = htole32(next_data_transfer_handle); 402 resp->transfer_flag = transfer_flag; 403 404 if (completion_code == PLDM_SUCCESS) { 405 memcpy(resp->fru_structure_data, fru_structure_data, data_size); 406 } 407 408 return PLDM_SUCCESS; 409 } 410 411 LIBPLDM_ABI_STABLE 412 int decode_get_fru_record_by_option_resp( 413 const struct pldm_msg *msg, size_t payload_length, 414 uint8_t *completion_code, uint32_t *next_transfer_handle, 415 uint8_t *transfer_flag, struct variable_field *fru_structure_data) 416 { 417 if (msg == NULL || completion_code == NULL || 418 next_transfer_handle == NULL || transfer_flag == NULL || 419 fru_structure_data == NULL) { 420 return PLDM_ERROR_INVALID_DATA; 421 } 422 423 *completion_code = msg->payload[0]; 424 if (PLDM_SUCCESS != *completion_code) { 425 return PLDM_SUCCESS; 426 } 427 428 if (payload_length < PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES) { 429 return PLDM_ERROR_INVALID_LENGTH; 430 } 431 432 struct pldm_get_fru_record_by_option_resp *resp = 433 (struct pldm_get_fru_record_by_option_resp *)msg->payload; 434 435 *next_transfer_handle = le32toh(resp->next_data_transfer_handle); 436 *transfer_flag = resp->transfer_flag; 437 fru_structure_data->ptr = resp->fru_structure_data; 438 fru_structure_data->length = 439 payload_length - PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES; 440 441 return PLDM_SUCCESS; 442 } 443 444 LIBPLDM_ABI_STABLE 445 int encode_get_fru_record_table_req(uint8_t instance_id, 446 uint32_t data_transfer_handle, 447 uint8_t transfer_operation_flag, 448 struct pldm_msg *msg, size_t payload_length) 449 450 { 451 if (msg == NULL) { 452 return PLDM_ERROR_INVALID_DATA; 453 } 454 if (payload_length != sizeof(struct pldm_get_fru_record_table_req)) { 455 return PLDM_ERROR_INVALID_LENGTH; 456 } 457 458 struct pldm_header_info header = { 0 }; 459 header.msg_type = PLDM_REQUEST; 460 header.instance = instance_id; 461 header.pldm_type = PLDM_FRU; 462 header.command = PLDM_GET_FRU_RECORD_TABLE; 463 464 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 465 if (rc != PLDM_SUCCESS) { 466 return rc; 467 } 468 469 struct pldm_get_fru_record_table_req *req = 470 (struct pldm_get_fru_record_table_req *)msg->payload; 471 req->data_transfer_handle = htole32(data_transfer_handle); 472 req->transfer_operation_flag = transfer_operation_flag; 473 474 return PLDM_SUCCESS; 475 } 476 477 LIBPLDM_ABI_STABLE 478 int decode_get_fru_record_table_resp_safe( 479 const struct pldm_msg *msg, size_t payload_length, 480 uint8_t *completion_code, uint32_t *next_data_transfer_handle, 481 uint8_t *transfer_flag, uint8_t *fru_record_table_data, 482 size_t *fru_record_table_length, size_t max_fru_record_table_length) 483 { 484 if (msg == NULL || completion_code == NULL || 485 next_data_transfer_handle == NULL || transfer_flag == NULL || 486 fru_record_table_data == NULL || fru_record_table_length == NULL) { 487 return PLDM_ERROR_INVALID_DATA; 488 } 489 490 *completion_code = msg->payload[0]; 491 if (PLDM_SUCCESS != *completion_code) { 492 return PLDM_SUCCESS; 493 } 494 if (payload_length <= PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES) { 495 return PLDM_ERROR_INVALID_LENGTH; 496 } 497 498 struct pldm_get_fru_record_table_resp *resp = 499 (struct pldm_get_fru_record_table_resp *)msg->payload; 500 501 *next_data_transfer_handle = le32toh(resp->next_data_transfer_handle); 502 *transfer_flag = resp->transfer_flag; 503 504 *fru_record_table_length = 505 payload_length - PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES; 506 507 if (*fru_record_table_length > max_fru_record_table_length) { 508 return PLDM_ERROR_INVALID_LENGTH; 509 } 510 511 memcpy(fru_record_table_data, resp->fru_record_table_data, 512 *fru_record_table_length); 513 514 return PLDM_SUCCESS; 515 } 516 517 LIBPLDM_ABI_STABLE 518 int decode_get_fru_record_table_resp(const struct pldm_msg *msg, 519 size_t payload_length, 520 uint8_t *completion_code, 521 uint32_t *next_data_transfer_handle, 522 uint8_t *transfer_flag, 523 uint8_t *fru_record_table_data, 524 size_t *fru_record_table_length) 525 { 526 return decode_get_fru_record_table_resp_safe( 527 msg, payload_length, completion_code, next_data_transfer_handle, 528 transfer_flag, fru_record_table_data, fru_record_table_length, 529 (size_t)-1); 530 } 531 532 LIBPLDM_ABI_STABLE 533 int decode_set_fru_record_table_req(const struct pldm_msg *msg, 534 size_t payload_length, 535 uint32_t *data_transfer_handle, 536 uint8_t *transfer_flag, 537 struct variable_field *fru_table_data) 538 539 { 540 if (msg == NULL || data_transfer_handle == NULL || 541 transfer_flag == NULL || fru_table_data == NULL) { 542 return PLDM_ERROR_INVALID_DATA; 543 } 544 545 if (payload_length <= PLDM_SET_FRU_RECORD_TABLE_MIN_REQ_BYTES) { 546 return PLDM_ERROR_INVALID_LENGTH; 547 } 548 549 struct pldm_set_fru_record_table_req *req = 550 (struct pldm_set_fru_record_table_req *)msg->payload; 551 552 *data_transfer_handle = le32toh(req->data_transfer_handle); 553 *transfer_flag = req->transfer_flag; 554 fru_table_data->length = 555 payload_length - PLDM_SET_FRU_RECORD_TABLE_MIN_REQ_BYTES; 556 fru_table_data->ptr = req->fru_record_table_data; 557 558 return PLDM_SUCCESS; 559 } 560 561 LIBPLDM_ABI_STABLE 562 int encode_set_fru_record_table_resp(uint8_t instance_id, 563 uint8_t completion_code, 564 uint32_t next_data_transfer_handle, 565 size_t payload_length, 566 struct pldm_msg *msg) 567 { 568 if (msg == NULL) { 569 return PLDM_ERROR_INVALID_DATA; 570 } 571 if (payload_length != PLDM_SET_FRU_RECORD_TABLE_RESP_BYTES) { 572 return PLDM_ERROR_INVALID_LENGTH; 573 } 574 575 struct pldm_header_info header = { 0 }; 576 header.instance = instance_id; 577 header.msg_type = PLDM_RESPONSE; 578 header.pldm_type = PLDM_FRU; 579 header.command = PLDM_SET_FRU_RECORD_TABLE; 580 581 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 582 if (PLDM_SUCCESS != rc) { 583 return rc; 584 } 585 586 struct pldm_set_fru_record_table_resp *response = 587 (struct pldm_set_fru_record_table_resp *)msg->payload; 588 response->completion_code = completion_code; 589 response->next_data_transfer_handle = 590 htole32(next_data_transfer_handle); 591 592 return PLDM_SUCCESS; 593 } 594