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