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 using InternalFailure =
20     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
21 
22 constexpr auto hostEffecterJson = "dbus_to_host_effecter.json";
23 
24 void HostEffecterParser::populatePropVals(
25     const Json& dBusValues, std::vector<PropertyValue>& propertyValues,
26     const std::string& propertyType)
27 
28 {
29     for (const auto& elem : dBusValues)
30     {
31         auto value = jsonEntryToDbusVal(propertyType, elem);
32         propertyValues.emplace_back(value);
33     }
34 }
35 
36 void HostEffecterParser::parseEffecterJson(const std::string& jsonPath)
37 {
38     fs::path jsonDir(jsonPath);
39     if (!fs::exists(jsonDir) || fs::is_empty(jsonDir))
40     {
41         std::cerr << "Host Effecter json path does not exist, DIR=" << jsonPath
42                   << "\n";
43         return;
44     }
45 
46     fs::path jsonFilePath = jsonDir / hostEffecterJson;
47     if (!fs::exists(jsonFilePath))
48     {
49         std::cerr << "json does not exist, PATH=" << jsonFilePath << "\n";
50         throw InternalFailure();
51     }
52 
53     std::ifstream jsonFile(jsonFilePath);
54     auto data = Json::parse(jsonFile, nullptr, false);
55     if (data.is_discarded())
56     {
57         std::cerr << "Parsing json file failed, FILE=" << jsonFilePath << "\n";
58         throw InternalFailure();
59     }
60     const Json empty{};
61     const std::vector<Json> emptyList{};
62 
63     auto entries = data.value("entries", emptyList);
64     for (const auto& entry : entries)
65     {
66         EffecterInfo effecterInfo;
67         effecterInfo.mctpEid = entry.value("mctp_eid", 0xFF);
68         auto jsonEffecterInfo = entry.value("effecter_info", empty);
69         auto effecterId =
70             jsonEffecterInfo.value("effecterID", PLDM_INVALID_EFFECTER_ID);
71         effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0);
72         effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0);
73         effecterInfo.entityInstance =
74             jsonEffecterInfo.value("entityInstance", 0);
75         effecterInfo.compEffecterCnt =
76             jsonEffecterInfo.value("compositeEffecterCount", 0);
77         auto effecters = entry.value("effecters", emptyList);
78         for (const auto& effecter : effecters)
79         {
80             DBusEffecterMapping dbusInfo{};
81             auto jsonDbusInfo = effecter.value("dbus_info", empty);
82             dbusInfo.dbusMap.objectPath = jsonDbusInfo.value("object_path", "");
83             dbusInfo.dbusMap.interface = jsonDbusInfo.value("interface", "");
84             dbusInfo.dbusMap.propertyName =
85                 jsonDbusInfo.value("property_name", "");
86             dbusInfo.dbusMap.propertyType =
87                 jsonDbusInfo.value("property_type", "");
88             Json propertyValues = jsonDbusInfo["property_values"];
89 
90             populatePropVals(propertyValues, dbusInfo.propertyValues,
91                              dbusInfo.dbusMap.propertyType);
92 
93             const std::vector<uint8_t> emptyStates{};
94             auto state = effecter.value("state", empty);
95             dbusInfo.state.stateSetId = state.value("id", 0);
96             auto states = state.value("state_values", emptyStates);
97             if (dbusInfo.propertyValues.size() != states.size())
98             {
99                 std::cerr << "Number of states do not match with"
100                           << " number of D-Bus property values in the json. "
101                           << "Object path " << dbusInfo.dbusMap.objectPath
102                           << " and property " << dbusInfo.dbusMap.propertyName
103                           << " will not be monitored \n";
104                 continue;
105             }
106             for (const auto& s : states)
107             {
108                 dbusInfo.state.states.emplace_back(s);
109             }
110 
111             auto effecterInfoIndex = hostEffecterInfo.size();
112             auto dbusInfoIndex = effecterInfo.dbusInfo.size();
113             createHostEffecterMatch(
114                 dbusInfo.dbusMap.objectPath, dbusInfo.dbusMap.interface,
115                 effecterInfoIndex, dbusInfoIndex, effecterId);
116             effecterInfo.dbusInfo.emplace_back(std::move(dbusInfo));
117         }
118         hostEffecterInfo.emplace_back(std::move(effecterInfo));
119     }
120 }
121 
122 void HostEffecterParser::processHostEffecterChangeNotification(
123     const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
124     size_t dbusInfoIndex, uint16_t effecterId)
125 {
126     const auto& propertyName = hostEffecterInfo[effecterInfoIndex]
127                                    .dbusInfo[dbusInfoIndex]
128                                    .dbusMap.propertyName;
129 
130     const auto& it = chProperties.find(propertyName);
131 
132     if (it == chProperties.end())
133     {
134         return;
135     }
136 
137     if (effecterId == PLDM_INVALID_EFFECTER_ID)
138     {
139         constexpr auto localOrRemote = false;
140         effecterId = findStateEffecterId(
141             pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType,
142             hostEffecterInfo[effecterInfoIndex].entityInstance,
143             hostEffecterInfo[effecterInfoIndex].containerId,
144             hostEffecterInfo[effecterInfoIndex]
145                 .dbusInfo[dbusInfoIndex]
146                 .state.stateSetId,
147             localOrRemote);
148         if (effecterId == PLDM_INVALID_EFFECTER_ID)
149         {
150             std::cerr << "Effecter id not found in pdr repo \n";
151             return;
152         }
153     }
154     constexpr auto hostStateInterface =
155         "xyz.openbmc_project.State.Boot.Progress";
156     constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
157 
158     try
159     {
160         auto propVal = dbusHandler->getDbusPropertyVariant(
161             hostStatePath, "BootProgress", hostStateInterface);
162         const auto& currHostState = std::get<std::string>(propVal);
163         if ((currHostState != "xyz.openbmc_project.State.Boot.Progress."
164                               "ProgressStages.SystemInitComplete") &&
165             (currHostState != "xyz.openbmc_project.State.Boot.Progress."
166                               "ProgressStages.OSRunning") &&
167             (currHostState != "xyz.openbmc_project.State.Boot.Progress."
168                               "ProgressStages.OSStart") &&
169             (currHostState != "xyz.openbmc_project.State.Boot.Progress."
170                               "ProgressStages.SystemSetup"))
171         {
172             std::cout << "Host is not up. Current host state: "
173                       << currHostState.c_str() << "\n";
174             return;
175         }
176     }
177     catch (const sdbusplus::exception_t& e)
178     {
179         std::cerr << "Error in getting current host state. Will still "
180                      "continue to set the host effecter - "
181                   << e.what() << std::endl;
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