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 = std::format("0x{:04x}",
66                                             htole16(pcieData->vendorId));
67                 auto deviceId = std::format("0x{:04x}",
68                                             htole16(pcieData->deviceId));
69                 auto revisionId = std::format("0x{:02x}",
70                                               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 = std::format("0x{:04x}",
81                                                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("Failed to set '{PROPERTY}' property: {ERROR}", "PROPERTY",
162               propertyName, "ERROR", e);
163     }
164 }
165 
166 void Handler::setFirmwareUAK(const std::vector<uint8_t>& data)
167 {
168     using VPDManager = sdbusplus::client::com::ibm::vpd::Manager<>;
169 
170     static constexpr auto uakObjPath = "/com/ibm/VPD/Manager";
171     static constexpr auto fruPath =
172         "/xyz/openbmc_project/inventory/system/chassis/motherboard";
173 
174     auto& bus = pldm::utils::DBusHandler::getBus();
175     try
176     {
177         auto service = pldm::utils::DBusHandler().getService(
178             uakObjPath, VPDManager::interface);
179         auto method = bus.new_method_call(
180             service.c_str(), uakObjPath, VPDManager::interface, "WriteKeyword");
181         method.append(static_cast<sdbusplus::message::object_path>(fruPath),
182                       "UTIL", "D8", data);
183         bus.call_noreply(method);
184     }
185     catch (const std::exception& e)
186     {
187         error("Failed to make a DBus call to VPD manager: {ERROR}", "ERROR", e);
188     }
189 }
190 
191 } // namespace oem_ibm_fru
192 } // namespace responder
193 } // namespace pldm
194