libpldm: Add APIs to decode descriptor entries

PLDM firmware update package header has record descriptors with type,
length and value. The response of QueryDeviceIdentifiers command has
the same format. This patch provides the decode API to iterate the
descriptor entries and also to parse the vendor defined descriptor
value.

The descriptor identifier table is based of DSP0267_1.1.0. The
implementation is compatible with the earlier verions DSP0267_1.0.0
and DSP0267_1.0.1.

Tested: Unit tests passed

Signed-off-by: Tom Joseph <rushtotom@gmail.com>
Change-Id: I900d190db3248068dbe93090ce646fd63ec957eb
diff --git a/libpldm/tests/libpldm_firmware_update_test.cpp b/libpldm/tests/libpldm_firmware_update_test.cpp
index 3e35cf6..84adc15 100644
--- a/libpldm/tests/libpldm_firmware_update_test.cpp
+++ b/libpldm/tests/libpldm_firmware_update_test.cpp
@@ -1,3 +1,5 @@
+#include <cstring>

+

 #include "libpldm/base.h"

 #include "libpldm/firmware_update.h"

 

@@ -5,6 +7,223 @@
 

 constexpr auto hdrSize = sizeof(pldm_msg_hdr);

 

+TEST(DecodeDescriptors, goodPath3Descriptors)

+{

+    // In the descriptor data there are 3 descriptor entries

+    // 1) IANA enterprise ID

+    constexpr std::array<uint8_t, PLDM_FWUP_IANA_ENTERPRISE_ID_LENGTH> iana{

+        0x0a, 0x0b, 0x0c, 0xd};

+    // 2) UUID

+    constexpr std::array<uint8_t, PLDM_FWUP_UUID_LENGTH> uuid{

+        0x12, 0x44, 0xd2, 0x64, 0x8d, 0x7d, 0x47, 0x18,

+        0xa0, 0x30, 0xfc, 0x8a, 0x56, 0x58, 0x7d, 0x5b};

+    // 3) Vendor Defined

+    constexpr std::string_view vendorTitle{"OpenBMC"};

+    constexpr size_t vendorDescriptorLen = 2;

+    constexpr std::array<uint8_t, vendorDescriptorLen> vendorDescriptorData{

+        0x01, 0x02};

+

+    constexpr size_t vendorDefinedDescriptorLen =

+        sizeof(pldm_vendor_defined_descriptor_title_data()

+                   .vendor_defined_descriptor_title_str_type) +

+        sizeof(pldm_vendor_defined_descriptor_title_data()

+                   .vendor_defined_descriptor_title_str_len) +

+        vendorTitle.size() + vendorDescriptorData.size();

+

+    constexpr size_t descriptorsLength =

+        3 * (sizeof(pldm_descriptor_tlv().descriptor_type) +

+             sizeof(pldm_descriptor_tlv().descriptor_length)) +

+        iana.size() + uuid.size() + vendorDefinedDescriptorLen;

+

+    constexpr std::array<uint8_t, descriptorsLength> descriptors{

+        0x01, 0x00, 0x04, 0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x02, 0x00, 0x10,

+        0x00, 0x12, 0x44, 0xd2, 0x64, 0x8d, 0x7d, 0x47, 0x18, 0xa0, 0x30,

+        0xfc, 0x8a, 0x56, 0x58, 0x7d, 0x5b, 0xFF, 0xFF, 0x0B, 0x00, 0x01,

+        0x07, 0x4f, 0x70, 0x65, 0x6e, 0x42, 0x4d, 0x43, 0x01, 0x02};

+

+    size_t descriptorCount = 1;

+    size_t descriptorsRemainingLength = descriptorsLength;

+    int rc = 0;

+

+    while (descriptorsRemainingLength && (descriptorCount <= 3))

+    {

+        uint16_t descriptorType = 0;

+        uint16_t descriptorLen = 0;

+        variable_field descriptorData{};

+

+        rc = decode_descriptor_type_length_value(

+            descriptors.data() + descriptorsLength - descriptorsRemainingLength,

+            descriptorsRemainingLength, &descriptorType, &descriptorData);

+        EXPECT_EQ(rc, PLDM_SUCCESS);

+

+        if (descriptorCount == 1)

+        {

+            EXPECT_EQ(descriptorType, PLDM_FWUP_IANA_ENTERPRISE_ID);

+            EXPECT_EQ(descriptorData.length,

+                      PLDM_FWUP_IANA_ENTERPRISE_ID_LENGTH);

+            EXPECT_EQ(true,

+                      std::equal(descriptorData.ptr,

+                                 descriptorData.ptr + descriptorData.length,

+                                 iana.begin(), iana.end()));

+        }

+        else if (descriptorCount == 2)

+        {

+            EXPECT_EQ(descriptorType, PLDM_FWUP_UUID);

+            EXPECT_EQ(descriptorData.length, PLDM_FWUP_UUID_LENGTH);

+            EXPECT_EQ(true,

+                      std::equal(descriptorData.ptr,

+                                 descriptorData.ptr + descriptorData.length,

+                                 uuid.begin(), uuid.end()));

+        }

+        else if (descriptorCount == 3)

+        {

+            EXPECT_EQ(descriptorType, PLDM_FWUP_VENDOR_DEFINED);

+            EXPECT_EQ(descriptorData.length, vendorDefinedDescriptorLen);

+

+            uint8_t descriptorTitleStrType = 0;

+            variable_field descriptorTitleStr{};

+            variable_field vendorDefinedDescriptorData{};

+

+            rc = decode_vendor_defined_descriptor_value(

+                descriptorData.ptr, descriptorData.length,

+                &descriptorTitleStrType, &descriptorTitleStr,

+                &vendorDefinedDescriptorData);

+            EXPECT_EQ(rc, PLDM_SUCCESS);

+

+            EXPECT_EQ(descriptorTitleStrType, PLDM_STR_TYPE_ASCII);

+            EXPECT_EQ(descriptorTitleStr.length, vendorTitle.size());

+            std::string vendorTitleStr(

+                reinterpret_cast<const char*>(descriptorTitleStr.ptr),

+                descriptorTitleStr.length);

+            EXPECT_EQ(vendorTitleStr, vendorTitle);

+

+            EXPECT_EQ(vendorDefinedDescriptorData.length,

+                      vendorDescriptorData.size());

+            EXPECT_EQ(true, std::equal(vendorDefinedDescriptorData.ptr,

+                                       vendorDefinedDescriptorData.ptr +

+                                           vendorDefinedDescriptorData.length,

+                                       vendorDescriptorData.begin(),

+                                       vendorDescriptorData.end()));

+        }

+

+        descriptorsRemainingLength -= sizeof(descriptorType) +

+                                      sizeof(descriptorLen) +

+                                      descriptorData.length;

+        descriptorCount++;

+    }

+}

+

+TEST(DecodeDescriptors, errorPathDecodeDescriptorTLV)

+{

+    int rc = 0;

+    // IANA Enterprise ID descriptor length incorrect

+    constexpr std::array<uint8_t, 7> invalidIANADescriptor1{

+        0x01, 0x00, 0x03, 0x00, 0x0a, 0x0b, 0x0c};

+    uint16_t descriptorType = 0;

+    variable_field descriptorData{};

+

+    rc = decode_descriptor_type_length_value(nullptr,

+                                             invalidIANADescriptor1.size(),

+                                             &descriptorType, &descriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_descriptor_type_length_value(invalidIANADescriptor1.data(),

+                                             invalidIANADescriptor1.size(),

+                                             nullptr, &descriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_descriptor_type_length_value(invalidIANADescriptor1.data(),

+                                             invalidIANADescriptor1.size(),

+                                             &descriptorType, nullptr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_descriptor_type_length_value(

+        invalidIANADescriptor1.data(), PLDM_FWUP_DEVICE_DESCRIPTOR_MIN_LEN - 1,

+        &descriptorType, &descriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    rc = decode_descriptor_type_length_value(invalidIANADescriptor1.data(),

+                                             invalidIANADescriptor1.size(),

+                                             &descriptorType, &descriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    // IANA Enterprise ID descriptor data less than length

+    std::array<uint8_t, 7> invalidIANADescriptor2{0x01, 0x00, 0x04, 0x00,

+                                                  0x0a, 0x0b, 0x0c};

+    rc = decode_descriptor_type_length_value(invalidIANADescriptor2.data(),

+                                             invalidIANADescriptor2.size(),

+                                             &descriptorType, &descriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+}

+

+TEST(DecodeDescriptors, errorPathVendorDefinedDescriptor)

+{

+    int rc = 0;

+    // VendorDefinedDescriptorTitleStringType is invalid

+    constexpr std::array<uint8_t, 9> invalidVendorDescriptor1{

+        0x06, 0x07, 0x4f, 0x70, 0x65, 0x6e, 0x42, 0x4d, 0x43};

+    uint8_t descriptorStringType = 0;

+    variable_field descriptorTitleStr{};

+    variable_field vendorDefinedDescriptorData{};

+

+    rc = decode_vendor_defined_descriptor_value(

+        nullptr, invalidVendorDescriptor1.size(), &descriptorStringType,

+        &descriptorTitleStr, &vendorDefinedDescriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_vendor_defined_descriptor_value(

+        invalidVendorDescriptor1.data(), invalidVendorDescriptor1.size(),

+        &descriptorStringType, &descriptorTitleStr,

+        &vendorDefinedDescriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_vendor_defined_descriptor_value(

+        invalidVendorDescriptor1.data(), invalidVendorDescriptor1.size(),

+        nullptr, &descriptorTitleStr, &vendorDefinedDescriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_vendor_defined_descriptor_value(

+        invalidVendorDescriptor1.data(), invalidVendorDescriptor1.size(),

+        &descriptorStringType, nullptr, &vendorDefinedDescriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_vendor_defined_descriptor_value(

+        invalidVendorDescriptor1.data(), invalidVendorDescriptor1.size(),

+        &descriptorStringType, &descriptorTitleStr, nullptr);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    rc = decode_vendor_defined_descriptor_value(

+        invalidVendorDescriptor1.data(),

+        sizeof(pldm_vendor_defined_descriptor_title_data) - 1,

+        &descriptorStringType, &descriptorTitleStr,

+        &vendorDefinedDescriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+

+    rc = decode_vendor_defined_descriptor_value(

+        invalidVendorDescriptor1.data(), invalidVendorDescriptor1.size(),

+        &descriptorStringType, &descriptorTitleStr,

+        &vendorDefinedDescriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    // VendorDefinedDescriptorTitleStringLength is 0

+    std::array<uint8_t, 9> invalidVendorDescriptor2{

+        0x01, 0x00, 0x4f, 0x70, 0x65, 0x6e, 0x42, 0x4d, 0x43};

+    rc = decode_vendor_defined_descriptor_value(

+        invalidVendorDescriptor2.data(), invalidVendorDescriptor2.size(),

+        &descriptorStringType, &descriptorTitleStr,

+        &vendorDefinedDescriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);

+

+    // VendorDefinedDescriptorData not present in the data

+    std::array<uint8_t, 9> invalidVendorDescriptor3{

+        0x01, 0x07, 0x4f, 0x70, 0x65, 0x6e, 0x42, 0x4d, 0x43};

+    rc = decode_vendor_defined_descriptor_value(

+        invalidVendorDescriptor3.data(), invalidVendorDescriptor3.size(),

+        &descriptorStringType, &descriptorTitleStr,

+        &vendorDefinedDescriptorData);

+    EXPECT_EQ(rc, PLDM_ERROR_INVALID_LENGTH);

+}

+

 TEST(QueryDeviceIdentifiers, goodPathEncodeRequest)

 {

     std::array<uint8_t, sizeof(pldm_msg_hdr)> requestMsg{};