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 
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     using BootProgress =
130         sdbusplus::client::xyz::openbmc_project::state::boot::Progress<>;
131 
132     const auto& propertyName = hostEffecterInfo[effecterInfoIndex]
133                                    .dbusInfo[dbusInfoIndex]
134                                    .dbusMap.propertyName;
135 
136     const auto& it = chProperties.find(propertyName);
137 
138     if (it == chProperties.end())
139     {
140         return;
141     }
142 
143     if (effecterId == PLDM_INVALID_EFFECTER_ID)
144     {
145         constexpr auto localOrRemote = false;
146         effecterId = findStateEffecterId(
147             pdrRepo, hostEffecterInfo[effecterInfoIndex].entityType,
148             hostEffecterInfo[effecterInfoIndex].entityInstance,
149             hostEffecterInfo[effecterInfoIndex].containerId,
150             hostEffecterInfo[effecterInfoIndex]
151                 .dbusInfo[dbusInfoIndex]
152                 .state.stateSetId,
153             localOrRemote);
154         if (effecterId == PLDM_INVALID_EFFECTER_ID)
155         {
156             error("Effecter id not found in pdr repo");
157             return;
158         }
159     }
160     constexpr auto hostStatePath = "/xyz/openbmc_project/state/host0";
161 
162     try
163     {
164         auto propVal = dbusHandler->getDbusPropertyVariant(
165             hostStatePath, "BootProgress", BootProgress::interface);
166 
167         using Stages = BootProgress::ProgressStages;
168         auto currHostState = sdbusplus::message::convert_from_string<Stages>(
169                                  std::get<std::string>(propVal))
170                                  .value();
171 
172         if (currHostState != Stages::SystemInitComplete &&
173             currHostState != Stages::OSRunning &&
174             currHostState != Stages::SystemSetup)
175         {
176             info("Host is not up. Current host state: {CUR_HOST_STATE}",
177                  "CUR_HOST_STATE", currHostState);
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 = findNewStateValue(effecterInfoIndex, dbusInfoIndex,
191                                      it->second);
192     }
193     catch (const std::out_of_range& e)
194     {
195         error("New state not found in json: {ERROR}", "ERROR", e);
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 = instanceIdDb->next(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         instanceIdDb->free(mctpEid, instanceId);
273         return rc;
274     }
275 
276     auto setStateEffecterStatesRespHandler =
277         [](mctp_eid_t /*eid*/, const pldm_msg* response, size_t respMsgLen) {
278         if (response == nullptr || !respMsgLen)
279         {
280             error(
281                 "Failed to receive response for setStateEffecterStates command");
282             return;
283         }
284         uint8_t completionCode{};
285         auto rc = decode_set_state_effecter_states_resp(response, respMsgLen,
286                                                         &completionCode);
287         if (rc)
288         {
289             error("Failed to decode setStateEffecterStates response, rc {RC}",
290                   "RC", rc);
291             pldm::utils::reportError(
292                 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
293         }
294         if (completionCode)
295         {
296             error("Failed to set a Host effecter, cc = {CC}", "CC",
297                   static_cast<unsigned>(completionCode));
298             pldm::utils::reportError(
299                 "xyz.openbmc_project.bmc.pldm.SetHostEffecterFailed");
300         }
301     };
302 
303     rc = handler->registerRequest(
304         mctpEid, instanceId, PLDM_PLATFORM, PLDM_SET_STATE_EFFECTER_STATES,
305         std::move(requestMsg), std::move(setStateEffecterStatesRespHandler));
306     if (rc)
307     {
308         error("Failed to send request to set an effecter on Host");
309     }
310     return rc;
311 }
312 
313 void HostEffecterParser::createHostEffecterMatch(const std::string& objectPath,
314                                                  const std::string& interface,
315                                                  size_t effecterInfoIndex,
316                                                  size_t dbusInfoIndex,
317                                                  uint16_t effecterId)
318 {
319     using namespace sdbusplus::bus::match::rules;
320     effecterInfoMatch.emplace_back(std::make_unique<sdbusplus::bus::match_t>(
321         pldm::utils::DBusHandler::getBus(),
322         propertiesChanged(objectPath, interface),
323         [this, effecterInfoIndex, dbusInfoIndex,
324          effecterId](sdbusplus::message_t& msg) {
325         DbusChgHostEffecterProps props;
326         std::string iface;
327         msg.read(iface, props);
328         processHostEffecterChangeNotification(props, effecterInfoIndex,
329                                               dbusInfoIndex, effecterId);
330     }));
331 }
332 
333 } // namespace host_effecters
334 } // namespace pldm
335