1 #include "fru_oem_ibm.hpp"
2 
3 #include <com/ibm/VPD/Manager/client.hpp>
4 #include <phosphor-logging/lg2.hpp>
5 
6 #include <ranges>
7 
8 PHOSPHOR_LOG2_USING;
9 
10 namespace pldm
11 {
12 namespace responder
13 {
14 namespace oem_ibm_fru
15 {
16 
17 void pldm::responder::oem_ibm_fru::Handler::setIBMFruHandler(
18     pldm::responder::fru::Handler* handler)
19 {
20     fruHandler = handler;
21 }
22 
23 int pldm::responder::oem_ibm_fru::Handler::processOEMFRUTable(
24     const std::vector<uint8_t>& fruData)
25 {
26     uint8_t dataSize = 0;
27     const uint8_t* data = fruData.data();
28 
29     while (dataSize < fruData.size())
30     {
31         auto record =
32             reinterpret_cast<const pldm_fru_record_data_format*>(data);
33         if (!record)
34         {
35             return PLDM_ERROR_INVALID_DATA;
36         }
37 
38         auto& entityAssociationMap = getAssociateEntityMap();
39         uint16_t fruRSI = le16toh(record->record_set_id);
40 
41         dataSize += sizeof(pldm_fru_record_data_format) -
42                     sizeof(pldm_fru_record_tlv);
43         data += dataSize;
44 
45         for ([[maybe_unused]] const auto& i :
46              std::views::iota(0, (int)record->num_fru_fields))
47         {
48             auto tlv = reinterpret_cast<const pldm_fru_record_tlv*>(data);
49             if (!tlv)
50             {
51                 return PLDM_ERROR_INVALID_DATA;
52             }
53 
54             if (tlv->type == PLDM_OEM_FRU_FIELD_TYPE_PCIE_CONFIG_SPACE_DATA)
55             {
56                 auto pcieData =
57                     reinterpret_cast<const PcieConfigSpaceData*>(tlv->value);
58 
59                 if (!pcieData)
60                 {
61                     return PLDM_ERROR_INVALID_DATA;
62                 }
63                 auto vendorId = std::to_string(htole16(pcieData->vendorId));
64                 auto deviceId = std::to_string(htole16(pcieData->deviceId));
65                 auto revisionId = std::to_string(pcieData->revisionId);
66 
67                 std::stringstream ss;
68 
69                 for (const auto& ele : pcieData->classCode)
70                 {
71                     ss << std::setfill('0') << std::setw(2) << std::hex << ele;
72                 }
73                 std::string classCode = ss.str();
74 
75                 auto subSystemVendorId =
76                     std::to_string(htole16(pcieData->subSystemVendorId));
77                 auto subSystemId =
78                     std::to_string(htole16(pcieData->subSystemId));
79 
80                 updateDBusProperty(fruRSI, entityAssociationMap, vendorId,
81                                    deviceId, revisionId, classCode,
82                                    subSystemVendorId, subSystemId);
83             }
84 
85             if (tlv->type == PLDM_OEM_IBM_FRU_FIELD_TYPE_FIRMWARE_UAK)
86             {
87                 std::vector<uint8_t> value(&tlv->value[0],
88                                            &tlv->value[tlv->length]);
89                 setFirmwareUAK(value);
90             }
91             // length of tlv is removed from the structure pldm_fru_record_tlv
92             // and the new tlv length is added back.
93             dataSize += sizeof(pldm_fru_record_tlv) - sizeof(uint8_t) +
94                         tlv->length;
95             data += dataSize;
96         }
97     }
98 
99     return PLDM_SUCCESS;
100 }
101 
102 void Handler::updateDBusProperty(
103     uint16_t fruRSI, const AssociatedEntityMap& fruAssociationMap,
104     const std::string& vendorId, const std::string& deviceId,
105     const std::string& revisionId, const std::string& classCode,
106     const std::string& subSystemVendorId, const std::string& subSystemId)
107 {
108     uint16_t entityType{};
109     uint16_t entityInstanceNum{};
110     uint16_t containerId{};
111     uint16_t terminusHandle{};
112     const pldm_pdr_record* record{};
113 
114     record = pldm_pdr_fru_record_set_find_by_rsi(
115         pdrRepo, fruRSI, &terminusHandle, &entityType, &entityInstanceNum,
116         &containerId);
117 
118     if (record)
119     {
120         for (const auto& [key, value] : fruAssociationMap)
121         {
122             if (entityInstanceNum == value.entity_instance_num &&
123                 entityType == value.entity_type &&
124                 containerId == value.entity_container_id)
125             {
126                 if (!(pldm::responder::utils::checkIfIBMFru(key)))
127                 {
128                     pldm::utils::setFruPresence(key, true);
129                 }
130                 dbus_map_update(key, "Function0VendorId", vendorId);
131                 dbus_map_update(key, "Function0DeviceId", deviceId);
132                 dbus_map_update(key, "Function0RevisionId", revisionId);
133                 dbus_map_update(key, "Function0ClassCode", classCode);
134                 dbus_map_update(key, "Function0SubsystemVendorId",
135                                 subSystemVendorId);
136                 dbus_map_update(key, "Function0SubsystemId", subSystemId);
137             }
138         }
139     }
140 }
141 
142 void Handler::dbus_map_update(const std::string& adapterObjPath,
143                               const std::string& propertyName,
144                               const std::string& propValue)
145 {
146     pldm::utils::PropertyValue value = propValue;
147     pldm::utils::DBusMapping dbusMapping;
148     dbusMapping.objectPath = adapterObjPath;
149     dbusMapping.interface = "xyz.openbmc_project.Inventory.Item.PCIeDevice";
150     dbusMapping.propertyName = propertyName;
151     dbusMapping.propertyType = "string";
152     try
153     {
154         pldm::utils::DBusHandler().setDbusProperty(dbusMapping, value);
155     }
156     catch (const std::exception& e)
157     {
158         error("Failed to set '{PROPERTY}' property: {ERROR}", "PROPERTY",
159               propertyName, "ERROR", e);
160     }
161 }
162 
163 void Handler::setFirmwareUAK(const std::vector<uint8_t>& data)
164 {
165     using VPDManager = sdbusplus::client::com::ibm::vpd::Manager<>;
166 
167     static constexpr auto uakObjPath = "/com/ibm/VPD/Manager";
168     static constexpr auto fruPath =
169         "/xyz/openbmc_project/inventory/system/chassis/motherboard";
170 
171     auto& bus = pldm::utils::DBusHandler::getBus();
172     try
173     {
174         auto service = pldm::utils::DBusHandler().getService(
175             uakObjPath, VPDManager::interface);
176         auto method = bus.new_method_call(
177             service.c_str(), uakObjPath, VPDManager::interface, "WriteKeyword");
178         method.append(static_cast<sdbusplus::message::object_path>(fruPath),
179                       "UTIL", "D8", data);
180         bus.call_noreply(method);
181     }
182     catch (const std::exception& e)
183     {
184         error("Failed to make a DBus call to VPD manager: {ERROR}", "ERROR", e);
185     }
186 }
187 
188 } // namespace oem_ibm_fru
189 } // namespace responder
190 } // namespace pldm
191