1 #include "fru.hpp" 2 3 #include "libpldm/utils.h" 4 5 #include "common/utils.hpp" 6 7 #include <systemd/sd-journal.h> 8 9 #include <sdbusplus/bus.hpp> 10 11 #include <iostream> 12 #include <set> 13 14 namespace pldm 15 { 16 17 namespace responder 18 { 19 20 FruImpl::FruImpl(const std::string& configPath, pldm_pdr* pdrRepo, 21 pldm_entity_association_tree* entityTree) : 22 pdrRepo(pdrRepo), 23 entityTree(entityTree) 24 { 25 fru_parser::FruParser handle(configPath); 26 27 fru_parser::DBusLookupInfo dbusInfo; 28 // Read the all the inventory D-Bus objects 29 auto& bus = pldm::utils::DBusHandler::getBus(); 30 dbus::ObjectValueTree objects; 31 32 try 33 { 34 dbusInfo = handle.inventoryLookup(); 35 auto method = bus.new_method_call( 36 std::get<0>(dbusInfo).c_str(), std::get<1>(dbusInfo).c_str(), 37 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 38 auto reply = bus.call(method); 39 reply.read(objects); 40 } 41 catch (const std::exception& e) 42 { 43 std::cerr << "Look up of inventory objects failed and PLDM FRU table " 44 "creation failed"; 45 return; 46 } 47 48 auto itemIntfsLookup = std::get<2>(dbusInfo); 49 50 for (const auto& object : objects) 51 { 52 const auto& interfaces = object.second; 53 54 for (const auto& interface : interfaces) 55 { 56 if (itemIntfsLookup.find(interface.first) != itemIntfsLookup.end()) 57 { 58 // An exception will be thrown by getRecordInfo, if the item 59 // D-Bus interface name specified in FRU_Master.json does 60 // not have corresponding config jsons 61 try 62 { 63 pldm_entity entity{}; 64 entity.entity_type = handle.getEntityType(interface.first); 65 pldm_entity_node* parent = nullptr; 66 auto parentObj = pldm::utils::findParent(object.first.str); 67 // To add a FRU to the entity association tree, we need to 68 // determine if the FRU has a parent (D-Bus object). For eg 69 // /system/backplane's parent is /system. /system has no 70 // parent. Some D-Bus pathnames might just be namespaces 71 // (not D-Bus objects), so we need to iterate upwards until 72 // a parent is found, or we reach the root ("/"). 73 // Parents are always added first before children in the 74 // entity association tree. We're relying on the fact that 75 // the std::map containing object paths from the 76 // GetManagedObjects call will have a sorted pathname list. 77 do 78 { 79 auto iter = objToEntityNode.find(parentObj); 80 if (iter != objToEntityNode.end()) 81 { 82 parent = iter->second; 83 break; 84 } 85 parentObj = pldm::utils::findParent(parentObj); 86 } while (parentObj != "/"); 87 88 auto node = pldm_entity_association_tree_add( 89 entityTree, &entity, parent, 90 PLDM_ENTITY_ASSOCIAION_PHYSICAL); 91 objToEntityNode[object.first.str] = node; 92 93 auto recordInfos = handle.getRecordInfo(interface.first); 94 populateRecords(interfaces, recordInfos, entity); 95 break; 96 } 97 catch (const std::exception& e) 98 { 99 std::cout << "Config JSONs missing for the item " 100 "interface type, interface = " 101 << interface.first << "\n"; 102 break; 103 } 104 } 105 } 106 } 107 108 pldm_entity_association_pdr_add(entityTree, pdrRepo, false); 109 110 if (table.size()) 111 { 112 padBytes = utils::getNumPadBytes(table.size()); 113 table.resize(table.size() + padBytes, 0); 114 115 // Calculate the checksum 116 checksum = crc32(table.data(), table.size()); 117 } 118 } 119 120 void FruImpl::populateRecords( 121 const pldm::responder::dbus::InterfaceMap& interfaces, 122 const fru_parser::FruRecordInfos& recordInfos, const pldm_entity& entity) 123 { 124 // recordSetIdentifier for the FRU will be set when the first record gets 125 // added for the FRU 126 uint16_t recordSetIdentifier = 0; 127 auto numRecsCount = numRecs; 128 129 for (auto const& [recType, encType, fieldInfos] : recordInfos) 130 { 131 std::vector<uint8_t> tlvs; 132 uint8_t numFRUFields = 0; 133 for (auto const& [intf, prop, propType, fieldTypeNum] : fieldInfos) 134 { 135 try 136 { 137 auto propValue = interfaces.at(intf).at(prop); 138 if (propType == "bytearray") 139 { 140 auto byteArray = std::get<std::vector<uint8_t>>(propValue); 141 if (!byteArray.size()) 142 { 143 continue; 144 } 145 146 numFRUFields++; 147 tlvs.emplace_back(fieldTypeNum); 148 tlvs.emplace_back(byteArray.size()); 149 std::move(std::begin(byteArray), std::end(byteArray), 150 std::back_inserter(tlvs)); 151 } 152 else if (propType == "string") 153 { 154 auto str = std::get<std::string>(propValue); 155 if (!str.size()) 156 { 157 continue; 158 } 159 160 numFRUFields++; 161 tlvs.emplace_back(fieldTypeNum); 162 tlvs.emplace_back(str.size()); 163 std::move(std::begin(str), std::end(str), 164 std::back_inserter(tlvs)); 165 } 166 } 167 catch (const std::out_of_range& e) 168 { 169 continue; 170 } 171 } 172 173 if (tlvs.size()) 174 { 175 if (numRecs == numRecsCount) 176 { 177 recordSetIdentifier = nextRSI(); 178 pldm_pdr_add_fru_record_set( 179 pdrRepo, 0, recordSetIdentifier, entity.entity_type, 180 entity.entity_instance_num, entity.entity_container_id); 181 } 182 auto curSize = table.size(); 183 table.resize(curSize + recHeaderSize + tlvs.size()); 184 encode_fru_record(table.data(), table.size(), &curSize, 185 recordSetIdentifier, recType, numFRUFields, 186 encType, tlvs.data(), tlvs.size()); 187 numRecs++; 188 } 189 } 190 } 191 192 void FruImpl::getFRUTable(Response& response) 193 { 194 auto hdrSize = response.size(); 195 196 response.resize(hdrSize + table.size() + sizeof(checksum), 0); 197 std::copy(table.begin(), table.end(), response.begin() + hdrSize); 198 199 // Copy the checksum to response data 200 auto iter = response.begin() + hdrSize + table.size(); 201 std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum), 202 iter); 203 } 204 205 namespace fru 206 { 207 208 Response Handler::getFRURecordTableMetadata(const pldm_msg* request, 209 size_t /*payloadLength*/) 210 { 211 constexpr uint8_t major = 0x01; 212 constexpr uint8_t minor = 0x00; 213 constexpr uint32_t maxSize = 0xFFFFFFFF; 214 215 Response response(sizeof(pldm_msg_hdr) + 216 PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES, 217 0); 218 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 219 220 auto rc = encode_get_fru_record_table_metadata_resp( 221 request->hdr.instance_id, PLDM_SUCCESS, major, minor, maxSize, 222 impl.size(), impl.numRSI(), impl.numRecords(), impl.checkSum(), 223 responsePtr); 224 if (rc != PLDM_SUCCESS) 225 { 226 return ccOnlyResponse(request, rc); 227 } 228 229 return response; 230 } 231 232 Response Handler::getFRURecordTable(const pldm_msg* request, 233 size_t payloadLength) 234 { 235 if (payloadLength != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES) 236 { 237 return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH); 238 } 239 240 Response response( 241 sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES, 0); 242 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 243 244 auto rc = 245 encode_get_fru_record_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 246 0, PLDM_START_AND_END, responsePtr); 247 if (rc != PLDM_SUCCESS) 248 { 249 return ccOnlyResponse(request, rc); 250 } 251 252 impl.getFRUTable(response); 253 254 return response; 255 } 256 257 } // namespace fru 258 259 } // namespace responder 260 261 } // namespace pldm 262