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