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 void FruImpl::buildFRUTable() 21 { 22 23 if (isBuilt) 24 { 25 return; 26 } 27 28 fru_parser::DBusLookupInfo dbusInfo; 29 // Read the all the inventory D-Bus objects 30 auto& bus = pldm::utils::DBusHandler::getBus(); 31 dbus::ObjectValueTree objects; 32 33 try 34 { 35 dbusInfo = parser.inventoryLookup(); 36 auto method = bus.new_method_call( 37 std::get<0>(dbusInfo).c_str(), std::get<1>(dbusInfo).c_str(), 38 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 39 auto reply = bus.call(method); 40 reply.read(objects); 41 } 42 catch (const std::exception& e) 43 { 44 std::cerr << "Look up of inventory objects failed and PLDM FRU table " 45 "creation failed"; 46 return; 47 } 48 49 auto itemIntfsLookup = std::get<2>(dbusInfo); 50 51 for (const auto& object : objects) 52 { 53 const auto& interfaces = object.second; 54 55 for (const auto& interface : interfaces) 56 { 57 if (itemIntfsLookup.find(interface.first) != itemIntfsLookup.end()) 58 { 59 // An exception will be thrown by getRecordInfo, if the item 60 // D-Bus interface name specified in FRU_Master.json does 61 // not have corresponding config jsons 62 try 63 { 64 pldm_entity entity{}; 65 entity.entity_type = parser.getEntityType(interface.first); 66 pldm_entity_node* parent = nullptr; 67 auto parentObj = pldm::utils::findParent(object.first.str); 68 // To add a FRU to the entity association tree, we need to 69 // determine if the FRU has a parent (D-Bus object). For eg 70 // /system/backplane's parent is /system. /system has no 71 // parent. Some D-Bus pathnames might just be namespaces 72 // (not D-Bus objects), so we need to iterate upwards until 73 // a parent is found, or we reach the root ("/"). 74 // Parents are always added first before children in the 75 // entity association tree. We're relying on the fact that 76 // the std::map containing object paths from the 77 // GetManagedObjects call will have a sorted pathname list. 78 do 79 { 80 auto iter = objToEntityNode.find(parentObj); 81 if (iter != objToEntityNode.end()) 82 { 83 parent = iter->second; 84 break; 85 } 86 parentObj = pldm::utils::findParent(parentObj); 87 } while (parentObj != "/"); 88 89 auto node = pldm_entity_association_tree_add( 90 entityTree, &entity, parent, 91 PLDM_ENTITY_ASSOCIAION_PHYSICAL); 92 objToEntityNode[object.first.str] = node; 93 94 auto recordInfos = parser.getRecordInfo(interface.first); 95 populateRecords(interfaces, recordInfos, entity); 96 97 associatedEntityMap.emplace(object.first, entity); 98 break; 99 } 100 catch (const std::exception& e) 101 { 102 std::cout << "Config JSONs missing for the item " 103 "interface type, interface = " 104 << interface.first << "\n"; 105 break; 106 } 107 } 108 } 109 } 110 111 pldm_entity_association_pdr_add(entityTree, pdrRepo, false); 112 113 if (table.size()) 114 { 115 padBytes = utils::getNumPadBytes(table.size()); 116 table.resize(table.size() + padBytes, 0); 117 118 // Calculate the checksum 119 checksum = crc32(table.data(), table.size()); 120 } 121 isBuilt = true; 122 } 123 124 void FruImpl::populateRecords( 125 const pldm::responder::dbus::InterfaceMap& interfaces, 126 const fru_parser::FruRecordInfos& recordInfos, const pldm_entity& entity) 127 { 128 // recordSetIdentifier for the FRU will be set when the first record gets 129 // added for the FRU 130 uint16_t recordSetIdentifier = 0; 131 auto numRecsCount = numRecs; 132 133 for (auto const& [recType, encType, fieldInfos] : recordInfos) 134 { 135 std::vector<uint8_t> tlvs; 136 uint8_t numFRUFields = 0; 137 for (auto const& [intf, prop, propType, fieldTypeNum] : fieldInfos) 138 { 139 try 140 { 141 auto propValue = interfaces.at(intf).at(prop); 142 if (propType == "bytearray") 143 { 144 auto byteArray = std::get<std::vector<uint8_t>>(propValue); 145 if (!byteArray.size()) 146 { 147 continue; 148 } 149 150 numFRUFields++; 151 tlvs.emplace_back(fieldTypeNum); 152 tlvs.emplace_back(byteArray.size()); 153 std::move(std::begin(byteArray), std::end(byteArray), 154 std::back_inserter(tlvs)); 155 } 156 else if (propType == "string") 157 { 158 auto str = std::get<std::string>(propValue); 159 if (!str.size()) 160 { 161 continue; 162 } 163 164 numFRUFields++; 165 tlvs.emplace_back(fieldTypeNum); 166 tlvs.emplace_back(str.size()); 167 std::move(std::begin(str), std::end(str), 168 std::back_inserter(tlvs)); 169 } 170 } 171 catch (const std::out_of_range& e) 172 { 173 continue; 174 } 175 } 176 177 if (tlvs.size()) 178 { 179 if (numRecs == numRecsCount) 180 { 181 recordSetIdentifier = nextRSI(); 182 pldm_pdr_add_fru_record_set( 183 pdrRepo, 0, recordSetIdentifier, entity.entity_type, 184 entity.entity_instance_num, entity.entity_container_id); 185 } 186 auto curSize = table.size(); 187 table.resize(curSize + recHeaderSize + tlvs.size()); 188 encode_fru_record(table.data(), table.size(), &curSize, 189 recordSetIdentifier, recType, numFRUFields, 190 encType, tlvs.data(), tlvs.size()); 191 numRecs++; 192 } 193 } 194 } 195 196 void FruImpl::getFRUTable(Response& response) 197 { 198 auto hdrSize = response.size(); 199 200 response.resize(hdrSize + table.size() + sizeof(checksum), 0); 201 std::copy(table.begin(), table.end(), response.begin() + hdrSize); 202 203 // Copy the checksum to response data 204 auto iter = response.begin() + hdrSize + table.size(); 205 std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum), 206 iter); 207 } 208 209 int FruImpl::getFRURecordByOption(std::vector<uint8_t>& fruData, 210 uint16_t /* fruTableHandle */, 211 uint16_t recordSetIdentifer, 212 uint8_t recordType, uint8_t fieldType) 213 { 214 // FRU table is built lazily, build if not done. 215 buildFRUTable(); 216 217 /* 7 is sizeof(checksum,4) + padBytesMax(3) 218 * We can not know size of the record table got by options in advance, but 219 * it must be less than the source table. So it's safe to use sizeof the 220 * source table + 7 as the buffer length 221 */ 222 size_t recordTableSize = table.size() - padBytes + 7; 223 fruData.resize(recordTableSize, 0); 224 225 get_fru_record_by_option(table.data(), table.size() - padBytes, 226 fruData.data(), &recordTableSize, 227 recordSetIdentifer, recordType, fieldType); 228 229 if (recordTableSize == 0) 230 { 231 return PLDM_FRU_DATA_STRUCTURE_TABLE_UNAVAILABLE; 232 } 233 234 auto pads = utils::getNumPadBytes(recordTableSize); 235 auto sum = crc32(fruData.data(), recordTableSize + pads); 236 237 auto iter = fruData.begin() + recordTableSize + pads; 238 std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum), 239 iter); 240 fruData.resize(recordTableSize + pads + sizeof(sum)); 241 242 return PLDM_SUCCESS; 243 } 244 245 namespace fru 246 { 247 248 Response Handler::getFRURecordTableMetadata(const pldm_msg* request, 249 size_t /*payloadLength*/) 250 { 251 // FRU table is built lazily, build if not done. 252 buildFRUTable(); 253 254 constexpr uint8_t major = 0x01; 255 constexpr uint8_t minor = 0x00; 256 constexpr uint32_t maxSize = 0xFFFFFFFF; 257 258 Response response(sizeof(pldm_msg_hdr) + 259 PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES, 260 0); 261 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 262 263 auto rc = encode_get_fru_record_table_metadata_resp( 264 request->hdr.instance_id, PLDM_SUCCESS, major, minor, maxSize, 265 impl.size(), impl.numRSI(), impl.numRecords(), impl.checkSum(), 266 responsePtr); 267 if (rc != PLDM_SUCCESS) 268 { 269 return ccOnlyResponse(request, rc); 270 } 271 272 return response; 273 } 274 275 Response Handler::getFRURecordTable(const pldm_msg* request, 276 size_t payloadLength) 277 { 278 // FRU table is built lazily, build if not done. 279 buildFRUTable(); 280 281 if (payloadLength != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES) 282 { 283 return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH); 284 } 285 286 Response response( 287 sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES, 0); 288 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 289 290 auto rc = 291 encode_get_fru_record_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 292 0, PLDM_START_AND_END, responsePtr); 293 if (rc != PLDM_SUCCESS) 294 { 295 return ccOnlyResponse(request, rc); 296 } 297 298 impl.getFRUTable(response); 299 300 return response; 301 } 302 303 Response Handler::getFRURecordByOption(const pldm_msg* request, 304 size_t payloadLength) 305 { 306 if (payloadLength != sizeof(pldm_get_fru_record_by_option_req)) 307 { 308 return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH); 309 } 310 311 uint32_t retDataTransferHandle{}; 312 uint16_t retFruTableHandle{}; 313 uint16_t retRecordSetIdentifier{}; 314 uint8_t retRecordType{}; 315 uint8_t retFieldType{}; 316 uint8_t retTransferOpFlag{}; 317 318 auto rc = decode_get_fru_record_by_option_req( 319 request, payloadLength, &retDataTransferHandle, &retFruTableHandle, 320 &retRecordSetIdentifier, &retRecordType, &retFieldType, 321 &retTransferOpFlag); 322 323 if (rc != PLDM_SUCCESS) 324 { 325 return ccOnlyResponse(request, rc); 326 } 327 328 std::vector<uint8_t> fruData; 329 rc = impl.getFRURecordByOption(fruData, retFruTableHandle, 330 retRecordSetIdentifier, retRecordType, 331 retFieldType); 332 if (rc != PLDM_SUCCESS) 333 { 334 return ccOnlyResponse(request, rc); 335 } 336 337 auto respPayloadLength = 338 PLDM_GET_FRU_RECORD_BY_OPTION_MIN_RESP_BYTES + fruData.size(); 339 Response response(sizeof(pldm_msg_hdr) + respPayloadLength, 0); 340 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 341 342 rc = encode_get_fru_record_by_option_resp( 343 request->hdr.instance_id, PLDM_SUCCESS, 0, PLDM_START_AND_END, 344 fruData.data(), fruData.size(), responsePtr, respPayloadLength); 345 346 if (rc != PLDM_SUCCESS) 347 { 348 return ccOnlyResponse(request, rc); 349 } 350 351 return response; 352 } 353 354 } // namespace fru 355 356 } // namespace responder 357 358 } // namespace pldm 359