libpldmresponder: Implement GetFRURecordByOption
Implement handler for GetFRURecordByOption command
Signed-off-by: John Wang <wangzqbj@inspur.com>
Change-Id: If2bc90872b2521f1771aa800de6fbce569a5b339
diff --git a/libpldm/fru.c b/libpldm/fru.c
index 64c1aba..207d4de 100644
--- a/libpldm/fru.c
+++ b/libpldm/fru.c
@@ -201,6 +201,73 @@
return PLDM_SUCCESS;
}
+static bool is_table_end(const struct pldm_fru_record_data_format *p,
+ const void *table, size_t table_size)
+{
+ return p ==
+ (const struct pldm_fru_record_data_format *)((uint8_t *)table +
+ table_size);
+}
+
+void get_fru_record_by_option(const uint8_t *table, size_t table_size,
+ uint8_t *record_table, size_t *record_size,
+ uint16_t rsi, uint8_t rt, uint8_t ft)
+{
+ const struct pldm_fru_record_data_format *record_data_src =
+ (const struct pldm_fru_record_data_format *)table;
+ struct pldm_fru_record_data_format *record_data_dest;
+ int count = 0;
+
+ const struct pldm_fru_record_tlv *tlv;
+ size_t len;
+ uint8_t *pos = record_table;
+
+ while (!is_table_end(record_data_src, table, table_size)) {
+ if ((record_data_src->record_set_id != htole16(rsi) &&
+ rsi != 0) ||
+ (record_data_src->record_type != rt && rt != 0)) {
+ tlv = record_data_src->tlvs;
+ for (int i = 0; i < record_data_src->num_fru_fields;
+ i++) {
+ len = sizeof(*tlv) - 1 + tlv->length;
+ tlv = (const struct pldm_fru_record_tlv
+ *)((char *)tlv + len);
+ }
+ record_data_src =
+ (const struct pldm_fru_record_data_format *)(tlv);
+ continue;
+ }
+
+ len = sizeof(struct pldm_fru_record_data_format) -
+ sizeof(struct pldm_fru_record_tlv);
+
+ assert(pos - record_table + len < *record_size);
+ memcpy(pos, record_data_src, len);
+
+ record_data_dest = (struct pldm_fru_record_data_format *)pos;
+ pos += len;
+
+ tlv = record_data_src->tlvs;
+ count = 0;
+ for (int i = 0; i < record_data_src->num_fru_fields; i++) {
+ len = sizeof(*tlv) - 1 + tlv->length;
+ if (tlv->type == ft || ft == 0) {
+ assert(pos - record_table + len < *record_size);
+ memcpy(pos, tlv, len);
+ pos += len;
+ count++;
+ }
+ tlv = (const struct pldm_fru_record_tlv *)((char *)tlv +
+ len);
+ }
+ record_data_dest->num_fru_fields = count;
+ record_data_src =
+ (const struct pldm_fru_record_data_format *)(tlv);
+ }
+
+ *record_size = pos - record_table;
+}
+
int encode_get_fru_record_by_option_req(
uint8_t instance_id, uint32_t data_transfer_handle,
uint16_t fru_table_handle, uint16_t record_set_identifier,
diff --git a/libpldm/fru.h b/libpldm/fru.h
index 3ec9e43..3739937 100644
--- a/libpldm/fru.h
+++ b/libpldm/fru.h
@@ -20,6 +20,10 @@
#define FRU_TABLE_CHECKSUM_SIZE 4
+enum pldm_fru_completion_codes {
+ PLDM_FRU_DATA_STRUCTURE_TABLE_UNAVAILABLE = 0x85,
+};
+
/** @brief PLDM FRU commands
*/
enum pldm_fru_commands {
@@ -407,6 +411,19 @@
uint32_t *next_transfer_handle, uint8_t *transfer_flag,
struct variable_field *fru_structure_data);
+/** @brief Get FRU Record Table By Option
+ * @param[in] table - The source fru record table
+ * @param[in] table_size - Size of the source fru record table
+ * @param[out] record_table - Fru table fetched based on the input option
+ * @param[in/out] record_size - Size of the table fetched by fru record option
+ * @param[in] rsi - FRU record set identifier
+ * @param[in] rt - FRU record type
+ * @param[in] ft - FRU field type
+ */
+void get_fru_record_by_option(const uint8_t *table, size_t table_size,
+ uint8_t *record_table, size_t *record_size,
+ uint16_t rsi, uint8_t rt, uint8_t ft);
+
#ifdef __cplusplus
}
#endif
diff --git a/libpldmresponder/fru.cpp b/libpldmresponder/fru.cpp
index ce470ab..cb7d5f7 100644
--- a/libpldmresponder/fru.cpp
+++ b/libpldmresponder/fru.cpp
@@ -204,6 +204,42 @@
iter);
}
+int FruImpl::getFRURecordByOption(std::vector<uint8_t>& fruData,
+ uint16_t /* fruTableHandle */,
+ uint16_t recordSetIdentifer,
+ uint8_t recordType, uint8_t fieldType)
+{
+ // FRU table is built lazily, build if not done.
+ buildFRUTable();
+
+ /* 7 is sizeof(checksum,4) + padBytesMax(3)
+ * We can not know size of the record table got by options in advance, but
+ * it must be less than the source table. So it's safe to use sizeof the
+ * source table + 7 as the buffer length
+ */
+ size_t recordTableSize = table.size() - padBytes + 7;
+ fruData.resize(recordTableSize, 0);
+
+ get_fru_record_by_option(table.data(), table.size() - padBytes,
+ fruData.data(), &recordTableSize,
+ recordSetIdentifer, recordType, fieldType);
+
+ if (recordTableSize == 0)
+ {
+ return PLDM_FRU_DATA_STRUCTURE_TABLE_UNAVAILABLE;
+ }
+
+ auto pads = utils::getNumPadBytes(recordTableSize);
+ auto sum = crc32(fruData.data(), recordTableSize + pads);
+
+ auto iter = fruData.begin() + recordTableSize + pads;
+ std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum),
+ iter);
+ fruData.resize(recordTableSize + pads + sizeof(sum));
+
+ return PLDM_SUCCESS;
+}
+
namespace fru
{
@@ -262,6 +298,57 @@
return response;
}
+Response Handler::getFRURecordByOption(const pldm_msg* request,
+ size_t payloadLength)
+{
+ if (payloadLength != sizeof(pldm_get_fru_record_by_option_req))
+ {
+ return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH);
+ }
+
+ uint32_t retDataTransferHandle{};
+ uint16_t retFruTableHandle{};
+ uint16_t retRecordSetIdentifier{};
+ uint8_t retRecordType{};
+ uint8_t retFieldType{};
+ uint8_t retTransferOpFlag{};
+
+ auto rc = decode_get_fru_record_by_option_req(
+ request, payloadLength, &retDataTransferHandle, &retFruTableHandle,
+ &retRecordSetIdentifier, &retRecordType, &retFieldType,
+ &retTransferOpFlag);
+
+ if (rc != PLDM_SUCCESS)
+ {
+ return ccOnlyResponse(request, rc);
+ }
+
+ std::vector<uint8_t> fruData;
+ rc = impl.getFRURecordByOption(fruData, retFruTableHandle,
+ retRecordSetIdentifier, retRecordType,
+ retFieldType);
+ if (rc != PLDM_SUCCESS)
+ {
+ return ccOnlyResponse(request, rc);
+ }
+
+ auto respPayloadLength =
+ PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES + fruData.size();
+ Response response(sizeof(pldm_msg_hdr) + respPayloadLength, 0);
+ auto responsePtr = reinterpret_cast<pldm_msg*>(response.data());
+
+ rc = encode_get_fru_record_by_option_resp(
+ request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END,
+ fruData.data(), fruData.size(), responsePtr, respPayloadLength);
+
+ if (rc != PLDM_SUCCESS)
+ {
+ return ccOnlyResponse(request, rc);
+ }
+
+ return response;
+}
+
} // namespace fru
} // namespace responder
diff --git a/libpldmresponder/fru.hpp b/libpldmresponder/fru.hpp
index 2597dc4..b19519c 100644
--- a/libpldmresponder/fru.hpp
+++ b/libpldmresponder/fru.hpp
@@ -106,6 +106,18 @@
*/
void getFRUTable(Response& response);
+ /** @brief Get FRU Record Table By Option
+ * @param[out] response - Populate response with the FRU table got by
+ * options
+ * @param[in] fruTableHandle - The fru table handle
+ * @param[in] recordSetIdentifer - The record set identifier
+ * @param[in] recordType - The record type
+ * @param[in] fieldType - The field type
+ */
+ int getFRURecordByOption(Response& response, uint16_t fruTableHandle,
+ uint16_t recordSetIdentifer, uint8_t recordType,
+ uint8_t fieldType);
+
/** @brief FRU table is built by processing the D-Bus inventory namespace
* based on the config files for FRU. The table is populated based
* on the isBuilt flag.
@@ -166,6 +178,11 @@
return this->getFRURecordTable(request,
payloadLength);
});
+ handlers.emplace(PLDM_GET_FRU_RECORD_BY_OPTION,
+ [this](const pldm_msg* request, size_t payloadLength) {
+ return this->getFRURecordByOption(request,
+ payloadLength);
+ });
}
/** @brief Handler for Get FRURecordTableMetadata
@@ -195,6 +212,16 @@
impl.buildFRUTable();
}
+ /** @brief Handler for GetFRURecordByOption
+ *
+ * @param[in] request - Request message payload
+ * @param[in] payloadLength - Request payload length
+ *
+ * @return PLDM response message
+ */
+ Response getFRURecordByOption(const pldm_msg* request,
+ size_t payloadLength);
+
private:
FruImpl impl;
};