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