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_STABLE 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 if ((*curr_size + record_hdr_size + tlvs_size) != total_size) { 188 return PLDM_ERROR_INVALID_LENGTH; 189 } 190 191 struct pldm_fru_record_data_format *record = 192 (struct pldm_fru_record_data_format *)(fru_table + *curr_size); 193 record->record_set_id = htole16(record_set_id); 194 record->record_type = record_type; 195 record->num_fru_fields = num_frus; 196 record->encoding_type = encoding; 197 *curr_size += record_hdr_size; 198 199 if (tlvs) { 200 memcpy(fru_table + *curr_size, tlvs, tlvs_size); 201 *curr_size += tlvs_size; 202 } 203 204 return PLDM_SUCCESS; 205 } 206 207 static bool is_table_end(const struct pldm_fru_record_data_format *p, 208 const void *table, size_t table_size) 209 { 210 return p >= 211 (const struct pldm_fru_record_data_format *)((uint8_t *)table + 212 table_size); 213 } 214 215 LIBPLDM_ABI_STABLE 216 int get_fru_record_by_option(const uint8_t *table, size_t table_size, 217 uint8_t *record_table, size_t *record_size, 218 uint16_t rsi, uint8_t rt, uint8_t ft) 219 { 220 const struct pldm_fru_record_data_format *record_data_src = 221 (const struct pldm_fru_record_data_format *)table; 222 struct pldm_fru_record_data_format *record_data_dest; 223 int count = 0; 224 225 const struct pldm_fru_record_tlv *tlv; 226 size_t len; 227 uint8_t *pos = record_table; 228 229 while (!is_table_end(record_data_src, table, table_size)) { 230 if ((record_data_src->record_set_id != htole16(rsi) && 231 rsi != 0) || 232 (record_data_src->record_type != rt && rt != 0)) { 233 tlv = record_data_src->tlvs; 234 for (int i = 0; i < record_data_src->num_fru_fields; 235 i++) { 236 len = sizeof(*tlv) - 1 + tlv->length; 237 tlv = (const struct pldm_fru_record_tlv 238 *)((char *)tlv + len); 239 } 240 record_data_src = 241 (const struct pldm_fru_record_data_format 242 *)(tlv); 243 continue; 244 } 245 246 len = sizeof(struct pldm_fru_record_data_format) - 247 sizeof(struct pldm_fru_record_tlv); 248 249 if (pos - record_table + len >= *record_size) { 250 return PLDM_ERROR_INVALID_LENGTH; 251 } 252 memcpy(pos, record_data_src, len); 253 254 record_data_dest = (struct pldm_fru_record_data_format *)pos; 255 pos += len; 256 257 tlv = record_data_src->tlvs; 258 count = 0; 259 for (int i = 0; i < record_data_src->num_fru_fields; i++) { 260 len = sizeof(*tlv) - 1 + tlv->length; 261 if (tlv->type == ft || ft == 0) { 262 if (pos - record_table + len >= *record_size) { 263 return PLDM_ERROR_INVALID_LENGTH; 264 } 265 memcpy(pos, tlv, len); 266 pos += len; 267 count++; 268 } 269 tlv = (const struct pldm_fru_record_tlv *)((char *)tlv + 270 len); 271 } 272 record_data_dest->num_fru_fields = count; 273 record_data_src = 274 (const struct pldm_fru_record_data_format *)(tlv); 275 } 276 277 *record_size = pos - record_table; 278 279 return PLDM_SUCCESS; 280 } 281 282 LIBPLDM_ABI_STABLE 283 int encode_get_fru_record_by_option_req( 284 uint8_t instance_id, uint32_t data_transfer_handle, 285 uint16_t fru_table_handle, uint16_t record_set_identifier, 286 uint8_t record_type, uint8_t field_type, uint8_t transfer_op_flag, 287 struct pldm_msg *msg, size_t payload_length) 288 { 289 if (msg == NULL) { 290 return PLDM_ERROR_INVALID_DATA; 291 } 292 293 if (payload_length != 294 sizeof(struct pldm_get_fru_record_by_option_req)) { 295 return PLDM_ERROR_INVALID_LENGTH; 296 } 297 298 struct pldm_header_info header = { 0 }; 299 header.instance = instance_id; 300 header.msg_type = PLDM_REQUEST; 301 header.pldm_type = PLDM_FRU; 302 header.command = PLDM_GET_FRU_RECORD_BY_OPTION; 303 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 304 if (rc != PLDM_SUCCESS) { 305 return rc; 306 } 307 308 struct pldm_get_fru_record_by_option_req *req = 309 (struct pldm_get_fru_record_by_option_req *)msg->payload; 310 311 req->data_transfer_handle = htole32(data_transfer_handle); 312 req->fru_table_handle = htole16(fru_table_handle); 313 req->record_set_identifier = htole16(record_set_identifier); 314 req->record_type = record_type; 315 req->field_type = field_type; 316 req->transfer_op_flag = transfer_op_flag; 317 318 return PLDM_SUCCESS; 319 } 320 321 LIBPLDM_ABI_STABLE 322 int decode_get_fru_record_by_option_req( 323 const struct pldm_msg *msg, size_t payload_length, 324 uint32_t *data_transfer_handle, uint16_t *fru_table_handle, 325 uint16_t *record_set_identifier, uint8_t *record_type, 326 uint8_t *field_type, uint8_t *transfer_op_flag) 327 { 328 if (msg == NULL || data_transfer_handle == NULL || 329 fru_table_handle == NULL || record_set_identifier == NULL || 330 record_type == NULL || field_type == NULL || 331 transfer_op_flag == NULL) { 332 return PLDM_ERROR_INVALID_DATA; 333 } 334 335 if (payload_length != 336 sizeof(struct pldm_get_fru_record_by_option_req)) { 337 return PLDM_ERROR_INVALID_LENGTH; 338 } 339 340 struct pldm_get_fru_record_by_option_req *req = 341 (struct pldm_get_fru_record_by_option_req *)msg->payload; 342 343 *data_transfer_handle = le32toh(req->data_transfer_handle); 344 *fru_table_handle = le16toh(req->fru_table_handle); 345 *record_set_identifier = le16toh(req->record_set_identifier); 346 *record_type = req->record_type; 347 *field_type = req->field_type; 348 *transfer_op_flag = req->transfer_op_flag; 349 return PLDM_SUCCESS; 350 } 351 352 LIBPLDM_ABI_STABLE 353 int encode_get_fru_record_by_option_resp(uint8_t instance_id, 354 uint8_t completion_code, 355 uint32_t next_data_transfer_handle, 356 uint8_t transfer_flag, 357 const void *fru_structure_data, 358 size_t data_size, struct pldm_msg *msg, 359 size_t payload_length) 360 { 361 if (msg == NULL || fru_structure_data == NULL) { 362 return PLDM_ERROR_INVALID_DATA; 363 } 364 365 if (payload_length < PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES) { 366 return PLDM_ERROR_INVALID_LENGTH; 367 } 368 369 if (payload_length - PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES < 370 data_size) { 371 return PLDM_ERROR_INVALID_LENGTH; 372 } 373 374 struct pldm_header_info header = { 0 }; 375 header.instance = instance_id; 376 header.msg_type = PLDM_RESPONSE; 377 header.pldm_type = PLDM_FRU; 378 header.command = PLDM_GET_FRU_RECORD_BY_OPTION; 379 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 380 if (rc != PLDM_SUCCESS) { 381 return rc; 382 } 383 384 struct pldm_get_fru_record_by_option_resp *resp = 385 (struct pldm_get_fru_record_by_option_resp *)msg->payload; 386 387 resp->completion_code = completion_code; 388 resp->next_data_transfer_handle = htole32(next_data_transfer_handle); 389 resp->transfer_flag = transfer_flag; 390 391 if (completion_code == PLDM_SUCCESS) { 392 memcpy(resp->fru_structure_data, fru_structure_data, data_size); 393 } 394 395 return PLDM_SUCCESS; 396 } 397 398 LIBPLDM_ABI_STABLE 399 int decode_get_fru_record_by_option_resp( 400 const struct pldm_msg *msg, size_t payload_length, 401 uint8_t *completion_code, uint32_t *next_transfer_handle, 402 uint8_t *transfer_flag, struct variable_field *fru_structure_data) 403 { 404 if (msg == NULL || completion_code == NULL || 405 next_transfer_handle == NULL || transfer_flag == NULL || 406 fru_structure_data == NULL) { 407 return PLDM_ERROR_INVALID_DATA; 408 } 409 410 *completion_code = msg->payload[0]; 411 if (PLDM_SUCCESS != *completion_code) { 412 return PLDM_SUCCESS; 413 } 414 415 if (payload_length < PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES) { 416 return PLDM_ERROR_INVALID_LENGTH; 417 } 418 419 struct pldm_get_fru_record_by_option_resp *resp = 420 (struct pldm_get_fru_record_by_option_resp *)msg->payload; 421 422 *next_transfer_handle = le32toh(resp->next_data_transfer_handle); 423 *transfer_flag = resp->transfer_flag; 424 fru_structure_data->ptr = resp->fru_structure_data; 425 fru_structure_data->length = 426 payload_length - PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES; 427 428 return PLDM_SUCCESS; 429 } 430 431 LIBPLDM_ABI_STABLE 432 int encode_get_fru_record_table_req(uint8_t instance_id, 433 uint32_t data_transfer_handle, 434 uint8_t transfer_operation_flag, 435 struct pldm_msg *msg, size_t payload_length) 436 437 { 438 if (msg == NULL) { 439 return PLDM_ERROR_INVALID_DATA; 440 } 441 if (payload_length != sizeof(struct pldm_get_fru_record_table_req)) { 442 return PLDM_ERROR_INVALID_LENGTH; 443 } 444 445 struct pldm_header_info header = { 0 }; 446 header.msg_type = PLDM_REQUEST; 447 header.instance = instance_id; 448 header.pldm_type = PLDM_FRU; 449 header.command = PLDM_GET_FRU_RECORD_TABLE; 450 451 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 452 if (rc != PLDM_SUCCESS) { 453 return rc; 454 } 455 456 struct pldm_get_fru_record_table_req *req = 457 (struct pldm_get_fru_record_table_req *)msg->payload; 458 req->data_transfer_handle = htole32(data_transfer_handle); 459 req->transfer_operation_flag = transfer_operation_flag; 460 461 return PLDM_SUCCESS; 462 } 463 464 LIBPLDM_ABI_STABLE 465 int decode_get_fru_record_table_resp_safe( 466 const struct pldm_msg *msg, size_t payload_length, 467 uint8_t *completion_code, uint32_t *next_data_transfer_handle, 468 uint8_t *transfer_flag, uint8_t *fru_record_table_data, 469 size_t *fru_record_table_length, size_t max_fru_record_table_length) 470 { 471 if (msg == NULL || completion_code == NULL || 472 next_data_transfer_handle == NULL || transfer_flag == NULL || 473 fru_record_table_data == NULL || fru_record_table_length == NULL) { 474 return PLDM_ERROR_INVALID_DATA; 475 } 476 477 *completion_code = msg->payload[0]; 478 if (PLDM_SUCCESS != *completion_code) { 479 return PLDM_SUCCESS; 480 } 481 if (payload_length <= PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES) { 482 return PLDM_ERROR_INVALID_LENGTH; 483 } 484 485 struct pldm_get_fru_record_table_resp *resp = 486 (struct pldm_get_fru_record_table_resp *)msg->payload; 487 488 *next_data_transfer_handle = le32toh(resp->next_data_transfer_handle); 489 *transfer_flag = resp->transfer_flag; 490 491 *fru_record_table_length = 492 payload_length - PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES; 493 494 if (*fru_record_table_length > max_fru_record_table_length) { 495 return PLDM_ERROR_INVALID_LENGTH; 496 } 497 498 memcpy(fru_record_table_data, resp->fru_record_table_data, 499 *fru_record_table_length); 500 501 return PLDM_SUCCESS; 502 } 503 504 LIBPLDM_ABI_STABLE 505 int decode_get_fru_record_table_resp(const struct pldm_msg *msg, 506 size_t payload_length, 507 uint8_t *completion_code, 508 uint32_t *next_data_transfer_handle, 509 uint8_t *transfer_flag, 510 uint8_t *fru_record_table_data, 511 size_t *fru_record_table_length) 512 { 513 return decode_get_fru_record_table_resp_safe( 514 msg, payload_length, completion_code, next_data_transfer_handle, 515 transfer_flag, fru_record_table_data, fru_record_table_length, 516 (size_t)-1); 517 } 518 519 LIBPLDM_ABI_STABLE 520 int decode_set_fru_record_table_req(const struct pldm_msg *msg, 521 size_t payload_length, 522 uint32_t *data_transfer_handle, 523 uint8_t *transfer_flag, 524 struct variable_field *fru_table_data) 525 526 { 527 if (msg == NULL || data_transfer_handle == NULL || 528 transfer_flag == NULL || fru_table_data == NULL) { 529 return PLDM_ERROR_INVALID_DATA; 530 } 531 532 if (payload_length <= PLDM_SET_FRU_RECORD_TABLE_MIN_REQ_BYTES) { 533 return PLDM_ERROR_INVALID_LENGTH; 534 } 535 536 struct pldm_set_fru_record_table_req *req = 537 (struct pldm_set_fru_record_table_req *)msg->payload; 538 539 *data_transfer_handle = le32toh(req->data_transfer_handle); 540 *transfer_flag = req->transfer_flag; 541 fru_table_data->length = 542 payload_length - PLDM_SET_FRU_RECORD_TABLE_MIN_REQ_BYTES; 543 fru_table_data->ptr = req->fru_record_table_data; 544 545 return PLDM_SUCCESS; 546 } 547 548 LIBPLDM_ABI_STABLE 549 int encode_set_fru_record_table_resp(uint8_t instance_id, 550 uint8_t completion_code, 551 uint32_t next_data_transfer_handle, 552 size_t payload_length, 553 struct pldm_msg *msg) 554 { 555 if (msg == NULL) { 556 return PLDM_ERROR_INVALID_DATA; 557 } 558 if (payload_length != PLDM_SET_FRU_RECORD_TABLE_RESP_BYTES) { 559 return PLDM_ERROR_INVALID_LENGTH; 560 } 561 562 struct pldm_header_info header = { 0 }; 563 header.instance = instance_id; 564 header.msg_type = PLDM_RESPONSE; 565 header.pldm_type = PLDM_FRU; 566 header.command = PLDM_SET_FRU_RECORD_TABLE; 567 568 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 569 if (PLDM_SUCCESS != rc) { 570 return rc; 571 } 572 573 struct pldm_set_fru_record_table_resp *response = 574 (struct pldm_set_fru_record_table_resp *)msg->payload; 575 response->completion_code = completion_code; 576 response->next_data_transfer_handle = 577 htole32(next_data_transfer_handle); 578 579 return PLDM_SUCCESS; 580 } 581