1 #include "dbus_to_host_effecters.hpp"
2 
3 #include <libpldm/pdr.h>
4 #include <libpldm/platform.h>
5 
6 #include <phosphor-logging/lg2.hpp>
7 #include <xyz/openbmc_project/Common/error.hpp>
8 #include <xyz/openbmc_project/State/Boot/Progress/client.hpp>
9 #include <xyz/openbmc_project/State/OperatingSystem/Status/server.hpp>
10 
11 #include <fstream>
12 
13 PHOSPHOR_LOG2_USING;
14 
15 using namespace pldm::utils;
16 
17 namespace pldm
18 {
19 namespace host_effecters
20 {
21 using InternalFailure =
22     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
23 
24 constexpr auto hostEffecterJson = "dbus_to_host_effecter.json";
25 
populatePropVals(const Json & dBusValues,std::vector<PropertyValue> & propertyValues,const std::string & propertyType)26 void HostEffecterParser::populatePropVals(
27     const Json& dBusValues, std::vector<PropertyValue>& propertyValues,
28     const std::string& propertyType)
29 
30 {
31     for (const auto& elem : dBusValues)
32     {
33         auto value = jsonEntryToDbusVal(propertyType, elem);
34         propertyValues.emplace_back(value);
35     }
36 }
37 
parseEffecterJson(const std::string & jsonPath)38 void HostEffecterParser::parseEffecterJson(const std::string& jsonPath)
39 {
40     fs::path jsonDir(jsonPath);
41     if (!fs::exists(jsonDir) || fs::is_empty(jsonDir))
42     {
43         error("Effecter json file for remote terminus '{PATH}' does not exist.",
44               "PATH", jsonPath);
45         return;
46     }
47 
48     fs::path jsonFilePath = jsonDir / hostEffecterJson;
49     if (!fs::exists(jsonFilePath))
50     {
51         error("Json at path '{PATH}' does not exist.", "PATH", jsonFilePath);
52         throw InternalFailure();
53     }
54 
55     std::ifstream jsonFile(jsonFilePath);
56     auto data = Json::parse(jsonFile, nullptr, false);
57     if (data.is_discarded())
58     {
59         error("Failed to parse json file {PATH}", "PATH", jsonFilePath);
60         throw InternalFailure();
61     }
62     const Json empty{};
63     const std::vector<Json> emptyList{};
64 
65     auto entries = data.value("entries", emptyList);
66     for (const auto& entry : entries)
67     {
68         EffecterInfo effecterInfo;
69         effecterInfo.mctpEid = entry.value("mctp_eid", 0xFF);
70         auto jsonEffecterInfo = entry.value("effecter_info", empty);
71         auto effecterId = jsonEffecterInfo.value("effecterID",
72                                                  PLDM_INVALID_EFFECTER_ID);
73         effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0);
74         effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0);
75         effecterInfo.entityInstance = jsonEffecterInfo.value("entityInstance",
76                                                              0);
77         effecterInfo.compEffecterCnt =
78             jsonEffecterInfo.value("compositeEffecterCount", 0);
79         auto effecters = entry.value("effecters", emptyList);
80         for (const auto& effecter : effecters)
81         {
82             DBusEffecterMapping dbusInfo{};
83             auto jsonDbusInfo = effecter.value("dbus_info", empty);
84             dbusInfo.dbusMap.objectPath = jsonDbusInfo.value("object_path", "");
85             dbusInfo.dbusMap.interface = jsonDbusInfo.value("interface", "");
86             dbusInfo.dbusMap.propertyName = jsonDbusInfo.value("property_name",
87                                                                "");
88             dbusInfo.dbusMap.propertyType = jsonDbusInfo.value("property_type",
89                                                                "");
90             Json propertyValues = jsonDbusInfo["property_values"];
91 
92             populatePropVals(propertyValues, dbusInfo.propertyValues,
93                              dbusInfo.dbusMap.propertyType);
94 
95             const std::vector<uint8_t> emptyStates{};
96             auto state = effecter.value("state", empty);
97             dbusInfo.state.stateSetId = state.value("id", 0);
98             auto states = state.value("state_values", emptyStates);
99             if (dbusInfo.propertyValues.size() != states.size())
100             {
101                 error(
102                     "Number of states do not match with number of D-Bus property values in the json. Object path at '{PATH}' and property '{PROPERTY}' will not be monitored",
103                     "PATH", dbusInfo.dbusMap.objectPath, "PROPERTY",
104                     dbusInfo.dbusMap.propertyName);
105                 continue;
106             }
107             for (const auto& s : states)
108             {
109                 dbusInfo.state.states.emplace_back(s);
110             }
111 
112             auto effecterInfoIndex = hostEffecterInfo.size();
113             auto dbusInfoIndex = effecterInfo.dbusInfo.size();
114             createHostEffecterMatch(
115                 dbusInfo.dbusMap.objectPath, dbusInfo.dbusMap.interface,
116                 effecterInfoIndex, dbusInfoIndex, effecterId);
117             effecterInfo.dbusInfo.emplace_back(std::move(dbusInfo));
118         }
119         hostEffecterInfo.emplace_back(std::move(effecterInfo));
120     }
121 }
122 
processHostEffecterChangeNotification(const DbusChgHostEffecterProps & chProperties,size_t effecterInfoIndex,size_t dbusInfoIndex,uint16_t effecterId)123 void HostEffecterParser::processHostEffecterChangeNotification(
124     const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
125     size_t dbusInfoIndex, uint16_t effecterId)
126 {
127     using BootProgress =
128         sdbusplus::client::xyz::openbmc_project::state::boot::Progress<>;
129 
130     const auto& propertyName = hostEffecterInfo[effecterInfoIndex]
131                                    .dbusInfo[dbusInfoIndex]
132                                    .dbusMap.propertyName;
133 
134     const auto& it = chProperties.find(propertyName);
135 
136     if (it == chProperties.end())
137     {
138         return;
139     }
140 
141     if (effecterId == PLDM_INVALID_EFFECTER_ID)
142     {
143         constexpr auto localOrRemote = false;
144         effecterId = findStateEffecterId(
145             pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType,
146             hostEffecterInfo[effecterInfoIndex].entityInstance,
147             hostEffecterInfo[effecterInfoIndex].containerId,
148             hostEffecterInfo[effecterInfoIndex]
149                 .dbusInfo[dbusInfoIndex]
150                 .state.stateSetId,
151             localOrRemote);
152         if (effecterId == PLDM_INVALID_EFFECTER_ID)
153         {
154             error(
155                 "Effecter ID '{EFFECTERID}' of entity type '{TYPE}', entityInstance '{INSTANCE}' and containerID '{CONTAINER_ID}' not found in pdr repo",
156                 "EFFECTERID", effecterId, "TYPE",
157                 hostEffecterInfo[effecterInfoIndex].entityType, "INSTANCE",
158                 hostEffecterInfo[effecterInfoIndex].entityInstance,
159                 "CONTAINER_ID",
160                 hostEffecterInfo[effecterInfoIndex].containerId);
161             return;
162         }
163     }
164     constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
165 
166     try
167     {
168         auto propVal = dbusHandler->getDbusPropertyVariant(
169             hostStatePath, "BootProgress", BootProgress::interface);
170 
171         using Stages = BootProgress::ProgressStages;
172         auto currHostState = sdbusplus::message::convert_from_string<Stages>(
173                                  std::get<std::string>(propVal))
174                                  .value();
175 
176         if (currHostState != Stages::SystemInitComplete &&
177             currHostState != Stages::OSRunning &&
178             currHostState != Stages::SystemSetup)
179         {
180             info(
181                 "Remote terminus is not up/active, current remote terminus state is: '{CURRENT_HOST_STATE}'",
182                 "CURRENT_HOST_STATE", currHostState);
183             return;
184         }
185     }
186     catch (const sdbusplus::exception_t& e)
187     {
188         error(
189             "Error in getting current remote terminus state. Will still continue to set the remote terminus effecter, error - {ERROR}",
190             "ERROR", e);
191     }
192     uint8_t newState{};
193     try
194     {
195         newState = findNewStateValue(effecterInfoIndex, dbusInfoIndex,
196                                      it->second);
197     }
198     catch (const std::out_of_range& e)
199     {
200         error("Failed to find new state '{NEW_STATE}' in json, error - {ERROR}",
201               "ERROR", e, "NEW_STATE", newState);
202         return;
203     }
204 
205     std::vector<set_effecter_state_field> stateField;
206     for (uint8_t i = 0; i < hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
207          i++)
208     {
209         if (i == dbusInfoIndex)
210         {
211             stateField.push_back({PLDM_REQUEST_SET, newState});
212         }
213         else
214         {
215             stateField.push_back({PLDM_NO_CHANGE, 0});
216         }
217     }
218     int rc{};
219     try
220     {
221         rc = setHostStateEffecter(effecterInfoIndex, stateField, effecterId);
222     }
223     catch (const std::runtime_error& e)
224     {
225         error(
226             "Failed to set remote terminus state effecter for effecter ID '{EFFECTERID}', error - {ERROR}",
227             "ERROR", e, "EFFECTERID", effecterId);
228         return;
229     }
230     if (rc != PLDM_SUCCESS)
231     {
232         error(
233             "Failed to set the remote terminus state effecter for effecter ID '{EFFECTERID}', response code '{RC}'",
234             "EFFECTERID", effecterId, "RC", rc);
235     }
236 }
237 
238 uint8_t
findNewStateValue(size_t effecterInfoIndex,size_t dbusInfoIndex,const PropertyValue & propertyValue)239     HostEffecterParser::findNewStateValue(size_t effecterInfoIndex,
240                                           size_t dbusInfoIndex,
241                                           const PropertyValue& propertyValue)
242 {
243     const auto& propValues = hostEffecterInfo[effecterInfoIndex]
244                                  .dbusInfo[dbusInfoIndex]
245                                  .propertyValues;
246     auto it = std::find(propValues.begin(), propValues.end(), propertyValue);
247     uint8_t newState{};
248     if (it != propValues.end())
249     {
250         auto index = std::distance(propValues.begin(), it);
251         newState = hostEffecterInfo[effecterInfoIndex]
252                        .dbusInfo[dbusInfoIndex]
253                        .state.states[index];
254     }
255     else
256     {
257         throw std::out_of_range("new state not found in json");
258     }
259     return newState;
260 }
261 
setHostStateEffecter(size_t effecterInfoIndex,std::vector<set_effecter_state_field> & stateField,uint16_t effecterId)262 int HostEffecterParser::setHostStateEffecter(
263     size_t effecterInfoIndex, std::vector<set_effecter_state_field>& stateField,
264     uint16_t effecterId)
265 {
266     uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid;
267     uint8_t& compEffCnt = hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
268     auto instanceId = instanceIdDb->next(mctpEid);
269 
270     std::vector<uint8_t> requestMsg(
271         sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(compEffCnt) +
272             sizeof(set_effecter_state_field) * compEffCnt,
273         0);
274     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
275     auto rc = encode_set_state_effecter_states_req(
276         instanceId, effecterId, compEffCnt, stateField.data(), request);
277 
278     if (rc != PLDM_SUCCESS)
279     {
280         error(
281             "Failed to encode set state effecter states message for effecter ID '{EFFECTERID}' and instanceID '{INSTANCE}' with response code '{RC}'",
282             "EFFECTERID", effecterId, "INSTANCE", instanceId, "RC", lg2::hex,
283             rc);
284         instanceIdDb->free(mctpEid, instanceId);
285         return rc;
286     }
287 
288     auto setStateEffecterStatesRespHandler =
289         [](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) {
290         if (response == nullptr || !respMsgLen)
291         {
292             error(
293                 "Failed to receive response for setting state effecter states.");
294             return;
295         }
296         uint8_t completionCode{};
297         auto rc = decode_set_state_effecter_states_resp(response, respMsgLen,
298                                                         &completionCode);
299         if (rc)
300         {
301             error(
302                 "Failed to decode response of set state effecter states, response code '{RC}'",
303                 "RC", rc);
304             pldm::utils::reportError(
305                 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
306         }
307         if (completionCode)
308         {
309             error(
310                 "Failed to set a remote terminus effecter, completion code '{CC}'",
311                 "CC", completionCode);
312             pldm::utils::reportError(
313                 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
314         }
315     };
316 
317     rc = handler->registerRequest(
318         mctpEid, instanceId, PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES,
319         std::move(requestMsg), std::move(setStateEffecterStatesRespHandler));
320     if (rc)
321     {
322         error(
323             "Failed to send request to set an effecter on remote terminus for effecter ID '{EFFECTERID}', response code '{RC}'",
324             "EFFECTERID", effecterId, "RC", rc);
325     }
326     return rc;
327 }
328 
createHostEffecterMatch(const std::string & objectPath,const std::string & interface,size_t effecterInfoIndex,size_t dbusInfoIndex,uint16_t effecterId)329 void HostEffecterParser::createHostEffecterMatch(const std::string& objectPath,
330                                                  const std::string& interface,
331                                                  size_t effecterInfoIndex,
332                                                  size_t dbusInfoIndex,
333                                                  uint16_t effecterId)
334 {
335     using namespace sdbusplus::bus::match::rules;
336     effecterInfoMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>(
337         pldm::utils::DBusHandler::getBus(),
338         propertiesChanged(objectPath, interface),
339         [this, effecterInfoIndex, dbusInfoIndex,
340          effecterId](sdbusplus::message_t& msg) {
341         DbusChgHostEffecterProps props;
342         std::string iface;
343         msg.read(iface, props);
344         processHostEffecterChangeNotification(props, effecterInfoIndex,
345                                               dbusInfoIndex, effecterId);
346     }));
347 }
348 
349 } // namespace host_effecters
350 } // namespace pldm
351