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