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 != 366 PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES + data_size) { 367 return PLDM_ERROR_INVALID_LENGTH; 368 } 369 370 struct pldm_header_info header = { 0 }; 371 header.instance = instance_id; 372 header.msg_type = PLDM_RESPONSE; 373 header.pldm_type = PLDM_FRU; 374 header.command = PLDM_GET_FRU_RECORD_BY_OPTION; 375 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 376 if (rc != PLDM_SUCCESS) { 377 return rc; 378 } 379 380 struct pldm_get_fru_record_by_option_resp *resp = 381 (struct pldm_get_fru_record_by_option_resp *)msg->payload; 382 383 resp->completion_code = completion_code; 384 resp->next_data_transfer_handle = htole32(next_data_transfer_handle); 385 resp->transfer_flag = transfer_flag; 386 387 if (completion_code == PLDM_SUCCESS) { 388 memcpy(resp->fru_structure_data, fru_structure_data, data_size); 389 } 390 391 return PLDM_SUCCESS; 392 } 393 394 LIBPLDM_ABI_STABLE 395 int decode_get_fru_record_by_option_resp( 396 const struct pldm_msg *msg, size_t payload_length, 397 uint8_t *completion_code, uint32_t *next_transfer_handle, 398 uint8_t *transfer_flag, struct variable_field *fru_structure_data) 399 { 400 if (msg == NULL || completion_code == NULL || 401 next_transfer_handle == NULL || transfer_flag == NULL || 402 fru_structure_data == NULL) { 403 return PLDM_ERROR_INVALID_DATA; 404 } 405 406 *completion_code = msg->payload[0]; 407 if (PLDM_SUCCESS != *completion_code) { 408 return PLDM_SUCCESS; 409 } 410 411 if (payload_length < PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES) { 412 return PLDM_ERROR_INVALID_LENGTH; 413 } 414 415 struct pldm_get_fru_record_by_option_resp *resp = 416 (struct pldm_get_fru_record_by_option_resp *)msg->payload; 417 418 *next_transfer_handle = le32toh(resp->next_data_transfer_handle); 419 *transfer_flag = resp->transfer_flag; 420 fru_structure_data->ptr = resp->fru_structure_data; 421 fru_structure_data->length = 422 payload_length - PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES; 423 424 return PLDM_SUCCESS; 425 } 426 427 LIBPLDM_ABI_STABLE 428 int encode_get_fru_record_table_req(uint8_t instance_id, 429 uint32_t data_transfer_handle, 430 uint8_t transfer_operation_flag, 431 struct pldm_msg *msg, size_t payload_length) 432 433 { 434 if (msg == NULL) { 435 return PLDM_ERROR_INVALID_DATA; 436 } 437 if (payload_length != sizeof(struct pldm_get_fru_record_table_req)) { 438 return PLDM_ERROR_INVALID_LENGTH; 439 } 440 441 struct pldm_header_info header = { 0 }; 442 header.msg_type = PLDM_REQUEST; 443 header.instance = instance_id; 444 header.pldm_type = PLDM_FRU; 445 header.command = PLDM_GET_FRU_RECORD_TABLE; 446 447 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 448 if (rc != PLDM_SUCCESS) { 449 return rc; 450 } 451 452 struct pldm_get_fru_record_table_req *req = 453 (struct pldm_get_fru_record_table_req *)msg->payload; 454 req->data_transfer_handle = htole32(data_transfer_handle); 455 req->transfer_operation_flag = transfer_operation_flag; 456 457 return PLDM_SUCCESS; 458 } 459 460 LIBPLDM_ABI_STABLE 461 int decode_get_fru_record_table_resp_safe( 462 const struct pldm_msg *msg, size_t payload_length, 463 uint8_t *completion_code, uint32_t *next_data_transfer_handle, 464 uint8_t *transfer_flag, uint8_t *fru_record_table_data, 465 size_t *fru_record_table_length, size_t max_fru_record_table_length) 466 { 467 if (msg == NULL || completion_code == NULL || 468 next_data_transfer_handle == NULL || transfer_flag == NULL || 469 fru_record_table_data == NULL || fru_record_table_length == NULL) { 470 return PLDM_ERROR_INVALID_DATA; 471 } 472 473 *completion_code = msg->payload[0]; 474 if (PLDM_SUCCESS != *completion_code) { 475 return PLDM_SUCCESS; 476 } 477 if (payload_length <= PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES) { 478 return PLDM_ERROR_INVALID_LENGTH; 479 } 480 481 struct pldm_get_fru_record_table_resp *resp = 482 (struct pldm_get_fru_record_table_resp *)msg->payload; 483 484 *next_data_transfer_handle = le32toh(resp->next_data_transfer_handle); 485 *transfer_flag = resp->transfer_flag; 486 487 *fru_record_table_length = 488 payload_length - PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES; 489 490 if (*fru_record_table_length > max_fru_record_table_length) { 491 return PLDM_ERROR_INVALID_LENGTH; 492 } 493 494 memcpy(fru_record_table_data, resp->fru_record_table_data, 495 *fru_record_table_length); 496 497 return PLDM_SUCCESS; 498 } 499 500 LIBPLDM_ABI_STABLE 501 int decode_get_fru_record_table_resp(const struct pldm_msg *msg, 502 size_t payload_length, 503 uint8_t *completion_code, 504 uint32_t *next_data_transfer_handle, 505 uint8_t *transfer_flag, 506 uint8_t *fru_record_table_data, 507 size_t *fru_record_table_length) 508 { 509 return decode_get_fru_record_table_resp_safe( 510 msg, payload_length, completion_code, next_data_transfer_handle, 511 transfer_flag, fru_record_table_data, fru_record_table_length, 512 (size_t)-1); 513 } 514 515 LIBPLDM_ABI_STABLE 516 int decode_set_fru_record_table_req(const struct pldm_msg *msg, 517 size_t payload_length, 518 uint32_t *data_transfer_handle, 519 uint8_t *transfer_flag, 520 struct variable_field *fru_table_data) 521 522 { 523 if (msg == NULL || data_transfer_handle == NULL || 524 transfer_flag == NULL || fru_table_data == NULL) { 525 return PLDM_ERROR_INVALID_DATA; 526 } 527 528 if (payload_length <= PLDM_SET_FRU_RECORD_TABLE_MIN_REQ_BYTES) { 529 return PLDM_ERROR_INVALID_LENGTH; 530 } 531 532 struct pldm_set_fru_record_table_req *req = 533 (struct pldm_set_fru_record_table_req *)msg->payload; 534 535 *data_transfer_handle = le32toh(req->data_transfer_handle); 536 *transfer_flag = req->transfer_flag; 537 fru_table_data->length = 538 payload_length - PLDM_SET_FRU_RECORD_TABLE_MIN_REQ_BYTES; 539 fru_table_data->ptr = req->fru_record_table_data; 540 541 return PLDM_SUCCESS; 542 } 543 544 LIBPLDM_ABI_STABLE 545 int encode_set_fru_record_table_resp(uint8_t instance_id, 546 uint8_t completion_code, 547 uint32_t next_data_transfer_handle, 548 size_t payload_length, 549 struct pldm_msg *msg) 550 { 551 if (msg == NULL) { 552 return PLDM_ERROR_INVALID_DATA; 553 } 554 if (payload_length != PLDM_SET_FRU_RECORD_TABLE_RESP_BYTES) { 555 return PLDM_ERROR_INVALID_LENGTH; 556 } 557 558 struct pldm_header_info header = { 0 }; 559 header.instance = instance_id; 560 header.msg_type = PLDM_RESPONSE; 561 header.pldm_type = PLDM_FRU; 562 header.command = PLDM_SET_FRU_RECORD_TABLE; 563 564 uint8_t rc = pack_pldm_header(&header, &(msg->hdr)); 565 if (PLDM_SUCCESS != rc) { 566 return rc; 567 } 568 569 struct pldm_set_fru_record_table_resp *response = 570 (struct pldm_set_fru_record_table_resp *)msg->payload; 571 response->completion_code = completion_code; 572 response->next_data_transfer_handle = 573 htole32(next_data_transfer_handle); 574 575 return PLDM_SUCCESS; 576 } 577