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 = jsonEffecterInfo.value("effecterID",
75                                                  PLDM_INVALID_EFFECTER_ID);
76         effecterInfo.containerId = jsonEffecterInfo.value("containerID", 0);
77         effecterInfo.entityType = jsonEffecterInfo.value("entityType", 0);
78         effecterInfo.entityInstance = jsonEffecterInfo.value("entityInstance",
79                                                              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 = jsonDbusInfo.value("property_name",
90                                                                "");
91             dbusInfo.dbusMap.propertyType = jsonDbusInfo.value("property_type",
92                                                                "");
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.SystemSetup"))
173         {
174             info("Host is not up. Current host state: {CUR_HOST_STATE}",
175                  "CUR_HOST_STATE", currHostState.c_str());
176             return;
177         }
178     }
179     catch (const sdbusplus::exception_t& e)
180     {
181         error(
182             "Error in getting current host state. Will still continue to set the host effecter - {ERR_EXCEP}",
183             "ERR_EXCEP", e.what());
184     }
185     uint8_t newState{};
186     try
187     {
188         newState = findNewStateValue(effecterInfoIndex, dbusInfoIndex,
189                                      it->second);
190     }
191     catch (const std::out_of_range& e)
192     {
193         error("New state not found in json");
194         return;
195     }
196 
197     std::vector<set_effecter_state_field> stateField;
198     for (uint8_t i = 0; i < hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
199          i++)
200     {
201         if (i == dbusInfoIndex)
202         {
203             stateField.push_back({PLDM_REQUEST_SET, newState});
204         }
205         else
206         {
207             stateField.push_back({PLDM_NO_CHANGE, 0});
208         }
209     }
210     int rc{};
211     try
212     {
213         rc = setHostStateEffecter(effecterInfoIndex, stateField, effecterId);
214     }
215     catch (const std::runtime_error& e)
216     {
217         error("Could not set host state effecter");
218         return;
219     }
220     if (rc != PLDM_SUCCESS)
221     {
222         error("Could not set the host state effecter, rc= {RC}", "RC", rc);
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 = instanceIdDb->next(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         error("Message encode failure. PLDM error code = {RC}", "RC", lg2::hex,
269               rc);
270         instanceIdDb->free(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             error(
279                 "Failed to receive response for setStateEffecterStates command");
280             return;
281         }
282         uint8_t completionCode{};
283         auto rc = decode_set_state_effecter_states_resp(response, respMsgLen,
284                                                         &completionCode);
285         if (rc)
286         {
287             error("Failed to decode setStateEffecterStates response, rc {RC}",
288                   "RC", rc);
289             pldm::utils::reportError(
290                 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
291         }
292         if (completionCode)
293         {
294             error("Failed to set a Host effecter, cc = {CC}", "CC",
295                   static_cast<unsigned>(completionCode));
296             pldm::utils::reportError(
297                 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
298         }
299     };
300 
301     rc = handler->registerRequest(
302         mctpEid, instanceId, PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES,
303         std::move(requestMsg), std::move(setStateEffecterStatesRespHandler));
304     if (rc)
305     {
306         error("Failed to send request to set an effecter on Host");
307     }
308     return rc;
309 }
310 
311 void HostEffecterParser::createHostEffecterMatch(const std::string& objectPath,
312                                                  const std::string& interface,
313                                                  size_t effecterInfoIndex,
314                                                  size_t dbusInfoIndex,
315                                                  uint16_t effecterId)
316 {
317     using namespace sdbusplus::bus::match::rules;
318     effecterInfoMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>(
319         pldm::utils::DBusHandler::getBus(),
320         propertiesChanged(objectPath, interface),
321         [this, effecterInfoIndex, dbusInfoIndex,
322          effecterId](sdbusplus::message_t& msg) {
323         DbusChgHostEffecterProps props;
324         std::string iface;
325         msg.read(iface, props);
326         processHostEffecterChangeNotification(props, effecterInfoIndex,
327                                               dbusInfoIndex, effecterId);
328         }));
329 }
330 
331 } // namespace host_effecters
332 } // namespace pldm
333