1 #include "dbus_to_host_effecters.hpp"
2 
3 #include "libpldm/pdr.h"
4 #include "libpldm/platform.h"
5 #include "libpldm/requester/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         {
171             std::cout << "Host is not up. Current host state: "
172                       << currHostState.c_str() << "\n";
173             return;
174         }
175     }
176     catch (const sdbusplus::exception::exception& e)
177     {
178         std::cerr << "Error in getting current host state. Will still "
179                      "continue to set the host effecter \n";
180     }
181     uint8_t newState{};
182     try
183     {
184         newState =
185             findNewStateValue(effecterInfoIndex, dbusInfoIndex, it->second);
186     }
187     catch (const std::out_of_range& e)
188     {
189         std::cerr << "new state not found in json"
190                   << "\n";
191         return;
192     }
193 
194     std::vector<set_effecter_state_field> stateField;
195     for (uint8_t i = 0; i < hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
196          i++)
197     {
198         if (i == dbusInfoIndex)
199         {
200             stateField.push_back({PLDM_REQUEST_SET, newState});
201         }
202         else
203         {
204             stateField.push_back({PLDM_NO_CHANGE, 0});
205         }
206     }
207     int rc{};
208     try
209     {
210         rc = setHostStateEffecter(effecterInfoIndex, stateField, effecterId);
211     }
212     catch (const std::runtime_error& e)
213     {
214         std::cerr << "Could not set host state effecter \n";
215         return;
216     }
217     if (rc != PLDM_SUCCESS)
218     {
219         std::cerr << "Could not set the host state effecter, rc= " << rc
220                   << " \n";
221     }
222 }
223 
224 uint8_t
225     HostEffecterParser::findNewStateValue(size_t effecterInfoIndex,
226                                           size_t dbusInfoIndex,
227                                           const PropertyValue& propertyValue)
228 {
229     const auto& propValues = hostEffecterInfo[effecterInfoIndex]
230                                  .dbusInfo[dbusInfoIndex]
231                                  .propertyValues;
232     auto it = std::find(propValues.begin(), propValues.end(), propertyValue);
233     uint8_t newState{};
234     if (it != propValues.end())
235     {
236         auto index = std::distance(propValues.begin(), it);
237         newState = hostEffecterInfo[effecterInfoIndex]
238                        .dbusInfo[dbusInfoIndex]
239                        .state.states[index];
240     }
241     else
242     {
243         throw std::out_of_range("new state not found in json");
244     }
245     return newState;
246 }
247 
248 int HostEffecterParser::setHostStateEffecter(
249     size_t effecterInfoIndex, std::vector<set_effecter_state_field>& stateField,
250     uint16_t effecterId)
251 {
252     uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid;
253     uint8_t& compEffCnt = hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
254     auto instanceId = requester->getInstanceId(mctpEid);
255 
256     std::vector<uint8_t> requestMsg(
257         sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(compEffCnt) +
258             sizeof(set_effecter_state_field) * compEffCnt,
259         0);
260     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
261     auto rc = encode_set_state_effecter_states_req(
262         instanceId, effecterId, compEffCnt, stateField.data(), request);
263 
264     if (rc != PLDM_SUCCESS)
265     {
266         std::cerr << "Message encode failure. PLDM error code = " << std::hex
267                   << std::showbase << rc << "\n";
268         requester->markFree(mctpEid, instanceId);
269         return rc;
270     }
271 
272     auto setStateEffecterStatesRespHandler =
273         [](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) {
274             if (response == nullptr || !respMsgLen)
275             {
276                 std::cerr << "Failed to receive response for "
277                           << "setStateEffecterStates command \n";
278                 return;
279             }
280             uint8_t completionCode{};
281             auto rc = decode_set_state_effecter_states_resp(
282                 response, respMsgLen, &completionCode);
283             if (rc)
284             {
285                 std::cerr << "Failed to decode setStateEffecterStates response,"
286                           << " rc " << rc << "\n";
287                 pldm::utils::reportError(
288                     "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
289             }
290             if (completionCode)
291             {
292                 std::cerr << "Failed to set a Host effecter "
293                           << ", cc=" << static_cast<unsigned>(completionCode)
294                           << "\n";
295                 pldm::utils::reportError(
296                     "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
297             }
298         };
299 
300     rc = handler->registerRequest(
301         mctpEid, instanceId, PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES,
302         std::move(requestMsg), std::move(setStateEffecterStatesRespHandler));
303     if (rc)
304     {
305         std::cerr << "Failed to send request to set an effecter on Host \n";
306     }
307     return rc;
308 }
309 
310 void HostEffecterParser::createHostEffecterMatch(const std::string& objectPath,
311                                                  const std::string& interface,
312                                                  size_t effecterInfoIndex,
313                                                  size_t dbusInfoIndex,
314                                                  uint16_t effecterId)
315 {
316     using namespace sdbusplus::bus::match::rules;
317     effecterInfoMatch.emplace_back(
318         std::make_unique<sdbusplus::bus::match::match>(
319             pldm::utils::DBusHandler::getBus(),
320             propertiesChanged(objectPath, interface),
321             [this, effecterInfoIndex, dbusInfoIndex,
322              effecterId](sdbusplus::message::message& msg) {
323                 DbusChgHostEffecterProps props;
324                 std::string iface;
325                 msg.read(iface, props);
326                 processHostEffecterChangeNotification(
327                     props, effecterInfoIndex, dbusInfoIndex, effecterId);
328             }));
329 }
330 
331 } // namespace host_effecters
332 } // namespace pldm
333