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