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 break; 97 } 98 catch (const std::exception& e) 99 { 100 std::cout << "Config JSONs missing for the item " 101 "interface type, interface = " 102 << interface.first << "\n"; 103 break; 104 } 105 } 106 } 107 } 108 109 pldm_entity_association_pdr_add(entityTree, pdrRepo, false); 110 111 if (table.size()) 112 { 113 padBytes = utils::getNumPadBytes(table.size()); 114 table.resize(table.size() + padBytes, 0); 115 116 // Calculate the checksum 117 checksum = crc32(table.data(), table.size()); 118 } 119 isBuilt = true; 120 } 121 122 void FruImpl::populateRecords( 123 const pldm::responder::dbus::InterfaceMap& interfaces, 124 const fru_parser::FruRecordInfos& recordInfos, const pldm_entity& entity) 125 { 126 // recordSetIdentifier for the FRU will be set when the first record gets 127 // added for the FRU 128 uint16_t recordSetIdentifier = 0; 129 auto numRecsCount = numRecs; 130 131 for (auto const& [recType, encType, fieldInfos] : recordInfos) 132 { 133 std::vector<uint8_t> tlvs; 134 uint8_t numFRUFields = 0; 135 for (auto const& [intf, prop, propType, fieldTypeNum] : fieldInfos) 136 { 137 try 138 { 139 auto propValue = interfaces.at(intf).at(prop); 140 if (propType == "bytearray") 141 { 142 auto byteArray = std::get<std::vector<uint8_t>>(propValue); 143 if (!byteArray.size()) 144 { 145 continue; 146 } 147 148 numFRUFields++; 149 tlvs.emplace_back(fieldTypeNum); 150 tlvs.emplace_back(byteArray.size()); 151 std::move(std::begin(byteArray), std::end(byteArray), 152 std::back_inserter(tlvs)); 153 } 154 else if (propType == "string") 155 { 156 auto str = std::get<std::string>(propValue); 157 if (!str.size()) 158 { 159 continue; 160 } 161 162 numFRUFields++; 163 tlvs.emplace_back(fieldTypeNum); 164 tlvs.emplace_back(str.size()); 165 std::move(std::begin(str), std::end(str), 166 std::back_inserter(tlvs)); 167 } 168 } 169 catch (const std::out_of_range& e) 170 { 171 continue; 172 } 173 } 174 175 if (tlvs.size()) 176 { 177 if (numRecs == numRecsCount) 178 { 179 recordSetIdentifier = nextRSI(); 180 pldm_pdr_add_fru_record_set( 181 pdrRepo, 0, recordSetIdentifier, entity.entity_type, 182 entity.entity_instance_num, entity.entity_container_id); 183 } 184 auto curSize = table.size(); 185 table.resize(curSize + recHeaderSize + tlvs.size()); 186 encode_fru_record(table.data(), table.size(), &curSize, 187 recordSetIdentifier, recType, numFRUFields, 188 encType, tlvs.data(), tlvs.size()); 189 numRecs++; 190 } 191 } 192 } 193 194 void FruImpl::getFRUTable(Response& response) 195 { 196 auto hdrSize = response.size(); 197 198 response.resize(hdrSize + table.size() + sizeof(checksum), 0); 199 std::copy(table.begin(), table.end(), response.begin() + hdrSize); 200 201 // Copy the checksum to response data 202 auto iter = response.begin() + hdrSize + table.size(); 203 std::copy_n(reinterpret_cast<const uint8_t*>(&checksum), sizeof(checksum), 204 iter); 205 } 206 207 namespace fru 208 { 209 210 Response Handler::getFRURecordTableMetadata(const pldm_msg* request, 211 size_t /*payloadLength*/) 212 { 213 // FRU table is built lazily, build if not done. 214 buildFRUTable(); 215 216 constexpr uint8_t major = 0x01; 217 constexpr uint8_t minor = 0x00; 218 constexpr uint32_t maxSize = 0xFFFFFFFF; 219 220 Response response(sizeof(pldm_msg_hdr) + 221 PLDM_GET_FRU_RECORD_TABLE_METADATA_RESP_BYTES, 222 0); 223 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 224 225 auto rc = encode_get_fru_record_table_metadata_resp( 226 request->hdr.instance_id, PLDM_SUCCESS, major, minor, maxSize, 227 impl.size(), impl.numRSI(), impl.numRecords(), impl.checkSum(), 228 responsePtr); 229 if (rc != PLDM_SUCCESS) 230 { 231 return ccOnlyResponse(request, rc); 232 } 233 234 return response; 235 } 236 237 Response Handler::getFRURecordTable(const pldm_msg* request, 238 size_t payloadLength) 239 { 240 // FRU table is built lazily, build if not done. 241 buildFRUTable(); 242 243 if (payloadLength != PLDM_GET_FRU_RECORD_TABLE_REQ_BYTES) 244 { 245 return ccOnlyResponse(request, PLDM_ERROR_INVALID_LENGTH); 246 } 247 248 Response response( 249 sizeof(pldm_msg_hdr) + PLDM_GET_FRU_RECORD_TABLE_MIN_RESP_BYTES, 0); 250 auto responsePtr = reinterpret_cast<pldm_msg*>(response.data()); 251 252 auto rc = 253 encode_get_fru_record_table_resp(request->hdr.instance_id, PLDM_SUCCESS, 254 0, PLDM_START_AND_END, responsePtr); 255 if (rc != PLDM_SUCCESS) 256 { 257 return ccOnlyResponse(request, rc); 258 } 259 260 impl.getFRUTable(response); 261 262 return response; 263 } 264 265 } // namespace fru 266 267 } // namespace responder 268 269 } // namespace pldm 270