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
setIBMFruHandler(pldm::responder::fru::Handler * handler)18 void pldm::responder::oem_ibm_fru::Handler::setIBMFruHandler(
19 pldm::responder::fru::Handler* handler)
20 {
21 fruHandler = handler;
22 }
23
processOEMFRUTable(const std::vector<uint8_t> & fruData)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
updateDBusProperty(uint16_t fruRSI,const AssociatedEntityMap & fruAssociationMap,const std::string & vendorId,const std::string & deviceId,const std::string & revisionId,const std::string & classCode,const std::string & subSystemVendorId,const std::string & subSystemId)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
dbus_map_update(const std::string & adapterObjPath,const std::string & propertyName,const std::string & propValue)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
setFirmwareUAK(const std::vector<uint8_t> & data)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