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/Boot/Progress/client.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     using BootProgress =
131         sdbusplus::client::xyz::openbmc_project::state::boot::Progress<>;
132 
133     const auto& propertyName = hostEffecterInfo[effecterInfoIndex]
134                                    .dbusInfo[dbusInfoIndex]
135                                    .dbusMap.propertyName;
136 
137     const auto& it = chProperties.find(propertyName);
138 
139     if (it == chProperties.end())
140     {
141         return;
142     }
143 
144     if (effecterId == PLDM_INVALID_EFFECTER_ID)
145     {
146         constexpr auto localOrRemote = false;
147         effecterId = findStateEffecterId(
148             pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType,
149             hostEffecterInfo[effecterInfoIndex].entityInstance,
150             hostEffecterInfo[effecterInfoIndex].containerId,
151             hostEffecterInfo[effecterInfoIndex]
152                 .dbusInfo[dbusInfoIndex]
153                 .state.stateSetId,
154             localOrRemote);
155         if (effecterId == PLDM_INVALID_EFFECTER_ID)
156         {
157             error("Effecter id not found in pdr repo");
158             return;
159         }
160     }
161     constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
162 
163     try
164     {
165         auto propVal = dbusHandler->getDbusPropertyVariant(
166             hostStatePath, "BootProgress", BootProgress::interface);
167 
168         using Stages = BootProgress::ProgressStages;
169         auto currHostState = sdbusplus::message::convert_from_string<Stages>(
170                                  std::get<std::string>(propVal))
171                                  .value();
172 
173         if (currHostState != Stages::SystemInitComplete &&
174             currHostState != Stages::OSRunning &&
175             currHostState != Stages::SystemSetup)
176         {
177             info("Host is not up. Current host state: {CUR_HOST_STATE}",
178                  "CUR_HOST_STATE", currHostState);
179             return;
180         }
181     }
182     catch (const sdbusplus::exception_t& e)
183     {
184         error(
185             "Error in getting current host state. Will still continue to set the host effecter - {ERR_EXCEP}",
186             "ERR_EXCEP", e.what());
187     }
188     uint8_t newState{};
189     try
190     {
191         newState = findNewStateValue(effecterInfoIndex, dbusInfoIndex,
192                                      it->second);
193     }
194     catch (const std::out_of_range& e)
195     {
196         error("New state not found in json: {ERROR}", "ERROR", e);
197         return;
198     }
199 
200     std::vector<set_effecter_state_field> stateField;
201     for (uint8_t i = 0; i < hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
202          i++)
203     {
204         if (i == dbusInfoIndex)
205         {
206             stateField.push_back({PLDM_REQUEST_SET, newState});
207         }
208         else
209         {
210             stateField.push_back({PLDM_NO_CHANGE, 0});
211         }
212     }
213     int rc{};
214     try
215     {
216         rc = setHostStateEffecter(effecterInfoIndex, stateField, effecterId);
217     }
218     catch (const std::runtime_error& e)
219     {
220         error("Could not set host state effecter");
221         return;
222     }
223     if (rc != PLDM_SUCCESS)
224     {
225         error("Could not set the host state effecter, rc= {RC}", "RC", rc);
226     }
227 }
228 
229 uint8_t
230     HostEffecterParser::findNewStateValue(size_t effecterInfoIndex,
231                                           size_t dbusInfoIndex,
232                                           const PropertyValue& propertyValue)
233 {
234     const auto& propValues = hostEffecterInfo[effecterInfoIndex]
235                                  .dbusInfo[dbusInfoIndex]
236                                  .propertyValues;
237     auto it = std::find(propValues.begin(), propValues.end(), propertyValue);
238     uint8_t newState{};
239     if (it != propValues.end())
240     {
241         auto index = std::distance(propValues.begin(), it);
242         newState = hostEffecterInfo[effecterInfoIndex]
243                        .dbusInfo[dbusInfoIndex]
244                        .state.states[index];
245     }
246     else
247     {
248         throw std::out_of_range("new state not found in json");
249     }
250     return newState;
251 }
252 
253 int HostEffecterParser::setHostStateEffecter(
254     size_t effecterInfoIndex, std::vector<set_effecter_state_field>& stateField,
255     uint16_t effecterId)
256 {
257     uint8_t& mctpEid = hostEffecterInfo[effecterInfoIndex].mctpEid;
258     uint8_t& compEffCnt = hostEffecterInfo[effecterInfoIndex].compEffecterCnt;
259     auto instanceId = instanceIdDb->next(mctpEid);
260 
261     std::vector<uint8_t> requestMsg(
262         sizeof(pldm_msg_hdr) + sizeof(effecterId) + sizeof(compEffCnt) +
263             sizeof(set_effecter_state_field) * compEffCnt,
264         0);
265     auto request = reinterpret_cast<pldm_msg*>(requestMsg.data());
266     auto rc = encode_set_state_effecter_states_req(
267         instanceId, effecterId, compEffCnt, stateField.data(), request);
268 
269     if (rc != PLDM_SUCCESS)
270     {
271         error("Message encode failure. PLDM error code = {RC}", "RC", lg2::hex,
272               rc);
273         instanceIdDb->free(mctpEid, instanceId);
274         return rc;
275     }
276 
277     auto setStateEffecterStatesRespHandler =
278         [](mctp_eid_t /*eid*/, const pldm_msg* response, 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