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/OperatingSystem/Status/server.hpp>
9 
10 #include <fstream>
11 #include <iostream>
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 
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 
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("Host Effecter json path does not exist, DIR = {JSON_PATH}",
44               "JSON_PATH", jsonPath.c_str());
45         return;
46     }
47 
48     fs::path jsonFilePath = jsonDir / hostEffecterJson;
49     if (!fs::exists(jsonFilePath))
50     {
51         error("json does not exist, PATH = {JSON_PATH}", "JSON_PATH",
52               jsonFilePath.c_str());
53         throw InternalFailure();
54     }
55 
56     std::ifstream jsonFile(jsonFilePath);
57     auto data = Json::parse(jsonFile, nullptr, false);
58     if (data.is_discarded())
59     {
60         error("Parsing json file failed, FILE = {JSON_PATH}", "JSON_PATH",
61               jsonFilePath.c_str());
62         throw InternalFailure();
63     }
64     const Json empty{};
65     const std::vector<Json> emptyList{};
66 
67     auto entries = data.value("entries", emptyList);
68     for (const auto& entry : entries)
69     {
70         EffecterInfo effecterInfo;
71         effecterInfo.mctpEid = entry.value("mctp_eid", 0xFF);
72         auto jsonEffecterInfo = entry.value("effecter_info", empty);
73         auto effecterId = jsonEffecterInfo.value("effecterID",
74                                                  PLDM_INVALID_EFFECTER_ID);
75         effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0);
76         effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0);
77         effecterInfo.entityInstance = jsonEffecterInfo.value("entityInstance",
78                                                              0);
79         effecterInfo.compEffecterCnt =
80             jsonEffecterInfo.value("compositeEffecterCount", 0);
81         auto effecters = entry.value("effecters", emptyList);
82         for (const auto& effecter : effecters)
83         {
84             DBusEffecterMapping dbusInfo{};
85             auto jsonDbusInfo = effecter.value("dbus_info", empty);
86             dbusInfo.dbusMap.objectPath = jsonDbusInfo.value("object_path", "");
87             dbusInfo.dbusMap.interface = jsonDbusInfo.value("interface", "");
88             dbusInfo.dbusMap.propertyName = jsonDbusInfo.value("property_name",
89                                                                "");
90             dbusInfo.dbusMap.propertyType = jsonDbusInfo.value("property_type",
91                                                                "");
92             Json propertyValues = jsonDbusInfo["property_values"];
93 
94             populatePropVals(propertyValues, dbusInfo.propertyValues,
95                              dbusInfo.dbusMap.propertyType);
96 
97             const std::vector<uint8_t> emptyStates{};
98             auto state = effecter.value("state", empty);
99             dbusInfo.state.stateSetId = state.value("id", 0);
100             auto states = state.value("state_values", emptyStates);
101             if (dbusInfo.propertyValues.size() != states.size())
102             {
103                 error(
104                     "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",
105                     "OBJ_PATH", dbusInfo.dbusMap.objectPath.c_str(),
106                     "PROP_NAME", dbusInfo.dbusMap.propertyName);
107                 continue;
108             }
109             for (const auto& s : states)
110             {
111                 dbusInfo.state.states.emplace_back(s);
112             }
113 
114             auto effecterInfoIndex = hostEffecterInfo.size();
115             auto dbusInfoIndex = effecterInfo.dbusInfo.size();
116             createHostEffecterMatch(
117                 dbusInfo.dbusMap.objectPath, dbusInfo.dbusMap.interface,
118                 effecterInfoIndex, dbusInfoIndex, effecterId);
119             effecterInfo.dbusInfo.emplace_back(std::move(dbusInfo));
120         }
121         hostEffecterInfo.emplace_back(std::move(effecterInfo));
122     }
123 }
124 
125 void HostEffecterParser::processHostEffecterChangeNotification(
126     const DbusChgHostEffecterProps& chProperties, size_t effecterInfoIndex,
127     size_t dbusInfoIndex, uint16_t effecterId)
128 {
129     const auto& propertyName = hostEffecterInfo[effecterInfoIndex]
130                                    .dbusInfo[dbusInfoIndex]
131                                    .dbusMap.propertyName;
132 
133     const auto& it = chProperties.find(propertyName);
134 
135     if (it == chProperties.end())
136     {
137         return;
138     }
139 
140     if (effecterId == PLDM_INVALID_EFFECTER_ID)
141     {
142         constexpr auto localOrRemote = false;
143         effecterId = findStateEffecterId(
144             pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType,
145             hostEffecterInfo[effecterInfoIndex].entityInstance,
146             hostEffecterInfo[effecterInfoIndex].containerId,
147             hostEffecterInfo[effecterInfoIndex]
148                 .dbusInfo[dbusInfoIndex]
149                 .state.stateSetId,
150             localOrRemote);
151         if (effecterId == PLDM_INVALID_EFFECTER_ID)
152         {
153             error("Effecter id not found in pdr repo");
154             return;
155         }
156     }
157     constexpr auto hostStateInterface =
158         "xyz.openbmc_project.State.Boot.Progress";
159     constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
160 
161     try
162     {
163         auto propVal = dbusHandler->getDbusPropertyVariant(
164             hostStatePath, "BootProgress", hostStateInterface);
165         const auto& currHostState = std::get<std::string>(propVal);
166         if ((currHostState != "xyz.openbmc_project.State.Boot.Progress."
167                               "ProgressStages.SystemInitComplete") &&
168             (currHostState != "xyz.openbmc_project.State.Boot.Progress."
169                               "ProgressStages.OSRunning") &&
170             (currHostState != "xyz.openbmc_project.State.Boot.Progress."
171                               "ProgressStages.SystemSetup"))
172         {
173             info("Host is not up. Current host state: {CUR_HOST_STATE}",
174                  "CUR_HOST_STATE", currHostState.c_str());
175             return;
176         }
177     }
178     catch (const sdbusplus::exception_t& e)
179     {
180         error(
181             "Error in getting current host state. Will still continue to set the host effecter - {ERR_EXCEP}",
182             "ERR_EXCEP", e.what());
183     }
184     uint8_t newState{};
185     try
186     {
187         newState = findNewStateValue(effecterInfoIndex, dbusInfoIndex,
188                                      it->second);
189     }
190     catch (const std::out_of_range& e)
191     {
192         error("New state not found in json");
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         error("Could not set host state effecter");
217         return;
218     }
219     if (rc != PLDM_SUCCESS)
220     {
221         error("Could not set the host state effecter, rc= {RC}", "RC", rc);
222     }
223 }
224 
225 uint8_t
226     HostEffecterParser::findNewStateValue(size_t effecterInfoIndex,
227                                           size_t dbusInfoIndex,
228                                           const PropertyValue& propertyValue)
229 {
230     const auto& propValues = hostEffecterInfo[effecterInfoIndex]
231                                  .dbusInfo[dbusInfoIndex]
232                                  .propertyValues;
233     auto it = std::find(propValues.begin(), propValues.end(), propertyValue);
234     uint8_t newState{};
235     if (it != propValues.end())
236     {
237         auto index = std::distance(propValues.begin(), it);
238         newState = hostEffecterInfo[effecterInfoIndex]
239                        .dbusInfo[dbusInfoIndex]
240                        .state.states[index];
241     }
242     else
243     {
244         throw std::out_of_range("new state not found in json");
245     }
246     return newState;
247 }
248 
249 int HostEffecterParser::setHostStateEffecter(
250     size_t effecterInfoIndex, std::vector<set_effecter_state_field>& stateField,
251     uint16_t effecterId)
252 {
253     uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid;
254     uint8_t& compEffCnt = hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
255     auto instanceId = instanceIdDb->next(mctpEid);
256 
257     std::vector<uint8_t> requestMsg(
258         sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(compEffCnt) +
259             sizeof(set_effecter_state_field) * compEffCnt,
260         0);
261     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
262     auto rc = encode_set_state_effecter_states_req(
263         instanceId, effecterId, compEffCnt, stateField.data(), request);
264 
265     if (rc != PLDM_SUCCESS)
266     {
267         error("Message encode failure. PLDM error code = {RC}", "RC", lg2::hex,
268               rc);
269         instanceIdDb->free(mctpEid, instanceId);
270         return rc;
271     }
272 
273     auto setStateEffecterStatesRespHandler =
274         [](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) {
275         if (response == nullptr || !respMsgLen)
276         {
277             error(
278                 "Failed to receive response for setStateEffecterStates command");
279             return;
280         }
281         uint8_t completionCode{};
282         auto rc = decode_set_state_effecter_states_resp(response, respMsgLen,
283                                                         &completionCode);
284         if (rc)
285         {
286             error("Failed to decode setStateEffecterStates response, rc {RC}",
287                   "RC", rc);
288             pldm::utils::reportError(
289                 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
290         }
291         if (completionCode)
292         {
293             error("Failed to set a Host effecter, cc = {CC}", "CC",
294                   static_cast<unsigned>(completionCode));
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         error("Failed to send request to set an effecter on Host");
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(std::make_unique<sdbusplus::bus::match_t>(
318         pldm::utils::DBusHandler::getBus(),
319         propertiesChanged(objectPath, interface),
320         [this, effecterInfoIndex, dbusInfoIndex,
321          effecterId](sdbusplus::message_t& msg) {
322         DbusChgHostEffecterProps props;
323         std::string iface;
324         msg.read(iface, props);
325         processHostEffecterChangeNotification(props, effecterInfoIndex,
326                                               dbusInfoIndex, effecterId);
327     }));
328 }
329 
330 } // namespace host_effecters
331 } // namespace pldm
332