1 #include "sensordatahandler.hpp"
2
3 #include "sensorhandler.hpp"
4
5 #include <ipmid/types.hpp>
6 #include <ipmid/utils.hpp>
7 #include <sdbusplus/message/types.hpp>
8 #include <xyz/openbmc_project/Common/error.hpp>
9
10 #include <bitset>
11 #include <filesystem>
12 #include <optional>
13
14 namespace ipmi
15 {
16 namespace sensor
17 {
18
19 using namespace phosphor::logging;
20 using InternalFailure =
21 sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
22
getAssertionSet(const SetSensorReadingReq & cmdData)23 AssertionSet getAssertionSet(const SetSensorReadingReq& cmdData)
24 {
25 Assertion assertionStates =
26 (static_cast<Assertion>(cmdData.assertOffset8_14)) << 8 |
27 cmdData.assertOffset0_7;
28 Deassertion deassertionStates =
29 (static_cast<Deassertion>(cmdData.deassertOffset8_14)) << 8 |
30 cmdData.deassertOffset0_7;
31 return std::make_pair(assertionStates, deassertionStates);
32 }
33
updateToDbus(IpmiUpdateData & msg)34 ipmi_ret_t updateToDbus(IpmiUpdateData& msg)
35 {
36 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
37 try
38 {
39 auto serviceResponseMsg = bus.call(msg);
40 }
41 catch (const InternalFailure& e)
42 {
43 lg2::error("Error in D-Bus call: {ERROR}", "ERROR", e);
44 commit<InternalFailure>();
45 return IPMI_CC_UNSPECIFIED_ERROR;
46 }
47 return IPMI_CC_OK;
48 }
49
50 namespace get
51 {
52
nameParentLeaf(const Info & sensorInfo)53 SensorName nameParentLeaf(const Info& sensorInfo)
54 {
55 const auto pos = sensorInfo.sensorPath.find_last_of('/');
56 const auto leaf = sensorInfo.sensorPath.substr(pos + 1);
57
58 const auto remaining = sensorInfo.sensorPath.substr(0, pos);
59
60 const auto parentPos = remaining.find_last_of('/');
61 auto parent = remaining.substr(parentPos + 1);
62
63 parent += "_" + leaf;
64 return parent;
65 }
66
mapDbusToAssertion(const Info & sensorInfo,const InstancePath & path,const DbusInterface & interface)67 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo,
68 const InstancePath& path,
69 const DbusInterface& interface)
70 {
71 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
72 GetSensorResponse response{};
73
74 enableScanning(&response);
75
76 auto service = ipmi::getService(bus, interface, path);
77
78 const auto& interfaceList = sensorInfo.propertyInterfaces;
79
80 for (const auto& interface : interfaceList)
81 {
82 for (const auto& property : interface.second)
83 {
84 auto propValue = ipmi::getDbusProperty(
85 bus, service, path, interface.first, property.first);
86
87 for (const auto& value : std::get<OffsetValueMap>(property.second))
88 {
89 if (propValue == value.second.assert)
90 {
91 setOffset(value.first, &response);
92 break;
93 }
94 }
95 }
96 }
97
98 return response;
99 }
100
mapDbusToEventdata2(const Info & sensorInfo)101 GetSensorResponse mapDbusToEventdata2(const Info& sensorInfo)
102 {
103 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
104 GetSensorResponse response{};
105
106 enableScanning(&response);
107
108 auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
109 sensorInfo.sensorPath);
110
111 const auto& interfaceList = sensorInfo.propertyInterfaces;
112
113 for (const auto& interface : interfaceList)
114 {
115 for (const auto& property : interface.second)
116 {
117 auto propValue =
118 ipmi::getDbusProperty(bus, service, sensorInfo.sensorPath,
119 interface.first, property.first);
120
121 for (const auto& value : std::get<OffsetValueMap>(property.second))
122 {
123 if (propValue == value.second.assert)
124 {
125 setReading(value.first, &response);
126 break;
127 }
128 }
129 }
130 }
131
132 return response;
133 }
134
135 #ifndef FEATURE_SENSORS_CACHE
assertion(const Info & sensorInfo)136 GetSensorResponse assertion(const Info& sensorInfo)
137 {
138 return mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath,
139 sensorInfo.sensorInterface);
140 }
141
eventdata2(const Info & sensorInfo)142 GetSensorResponse eventdata2(const Info& sensorInfo)
143 {
144 return mapDbusToEventdata2(sensorInfo);
145 }
146 #else
assertion(uint8_t id,const Info & sensorInfo,const PropertyMap &)147 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo,
148 const PropertyMap& /*properties*/)
149 {
150 // The assertion may contain multiple properties
151 // So we have to get the properties from DBus anyway
152 auto response = mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath,
153 sensorInfo.sensorInterface);
154
155 if (!sensorCacheMap[id].has_value())
156 {
157 sensorCacheMap[id] = SensorData{};
158 }
159 sensorCacheMap[id]->response = response;
160 return response;
161 }
162
eventdata2(uint8_t id,const Info & sensorInfo,const PropertyMap &)163 std::optional<GetSensorResponse> eventdata2(uint8_t id, const Info& sensorInfo,
164 const PropertyMap& /*properties*/)
165 {
166 // The eventdata2 may contain multiple properties
167 // So we have to get the properties from DBus anyway
168 auto response = mapDbusToEventdata2(sensorInfo);
169
170 if (!sensorCacheMap[id].has_value())
171 {
172 sensorCacheMap[id] = SensorData{};
173 }
174 sensorCacheMap[id]->response = response;
175 return response;
176 }
177
178 #endif // FEATURE_SENSORS_CACHE
179
180 } // namespace get
181
182 namespace set
183 {
184
makeDbusMsg(const std::string & updateInterface,const std::string & sensorPath,const std::string & command,const std::string & sensorInterface)185 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
186 const std::string& sensorPath,
187 const std::string& command,
188 const std::string& sensorInterface)
189 {
190 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
191 using namespace std::string_literals;
192
193 auto dbusService = getService(bus, sensorInterface, sensorPath);
194
195 return bus.new_method_call(dbusService.c_str(), sensorPath.c_str(),
196 updateInterface.c_str(), command.c_str());
197 }
198
eventdata(const SetSensorReadingReq &,const Info & sensorInfo,uint8_t data)199 ipmi_ret_t eventdata(const SetSensorReadingReq&, const Info& sensorInfo,
200 uint8_t data)
201 {
202 auto msg =
203 makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
204 "Set", sensorInfo.sensorInterface);
205
206 const auto& interface = sensorInfo.propertyInterfaces.begin();
207 msg.append(interface->first);
208 for (const auto& property : interface->second)
209 {
210 msg.append(property.first);
211 const auto& iter = std::get<OffsetValueMap>(property.second).find(data);
212 if (iter == std::get<OffsetValueMap>(property.second).end())
213 {
214 lg2::error("Invalid event data");
215 return IPMI_CC_PARM_OUT_OF_RANGE;
216 }
217 msg.append(iter->second.assert);
218 }
219 return updateToDbus(msg);
220 }
221
assertion(const SetSensorReadingReq & cmdData,const Info & sensorInfo)222 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
223 {
224 std::bitset<16> assertionSet(getAssertionSet(cmdData).first);
225 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second);
226 auto bothSet = assertionSet ^ deassertionSet;
227
228 const auto& interface = sensorInfo.propertyInterfaces.begin();
229
230 for (const auto& property : interface->second)
231 {
232 std::optional<Value> tmp;
233 for (const auto& value : std::get<OffsetValueMap>(property.second))
234 {
235 if (bothSet.size() <= value.first || !bothSet.test(value.first))
236 {
237 // A BIOS shouldn't do this but ignore if they do.
238 continue;
239 }
240
241 if (assertionSet.test(value.first))
242 {
243 tmp = value.second.assert;
244 break;
245 }
246 if (deassertionSet.test(value.first))
247 {
248 tmp = value.second.deassert;
249 break;
250 }
251 }
252
253 if (tmp)
254 {
255 auto msg = makeDbusMsg("org.freedesktop.DBus.Properties",
256 sensorInfo.sensorPath, "Set",
257 sensorInfo.sensorInterface);
258 msg.append(interface->first);
259 msg.append(property.first);
260 msg.append(*tmp);
261
262 auto rc = updateToDbus(msg);
263 if (rc)
264 {
265 return rc;
266 }
267 }
268 }
269
270 return IPMI_CC_OK;
271 }
272
273 } // namespace set
274
275 namespace notify
276 {
277
makeDbusMsg(const std::string & updateInterface,const std::string &,const std::string & command,const std::string &)278 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
279 const std::string&, const std::string& command,
280 const std::string&)
281 {
282 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
283 using namespace std::string_literals;
284
285 static const auto dbusPath = "/xyz/openbmc_project/inventory"s;
286 std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath);
287
288 return bus.new_method_call(dbusService.c_str(), dbusPath.c_str(),
289 updateInterface.c_str(), command.c_str());
290 }
291
assertion(const SetSensorReadingReq & cmdData,const Info & sensorInfo)292 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
293 {
294 auto msg = makeDbusMsg(sensorInfo.sensorInterface, sensorInfo.sensorPath,
295 "Notify", sensorInfo.sensorInterface);
296
297 std::bitset<16> assertionSet(getAssertionSet(cmdData).first);
298 std::bitset<16> deassertionSet(getAssertionSet(cmdData).second);
299 ipmi::sensor::ObjectMap objects;
300 ipmi::sensor::InterfaceMap interfaces;
301 for (const auto& interface : sensorInfo.propertyInterfaces)
302 {
303 // An interface with no properties - It is possible that the sensor
304 // object on DBUS implements a DBUS interface with no properties.
305 // Make sure we add the interface to the list if interfaces on the
306 // object with an empty property map.
307 if (interface.second.empty())
308 {
309 interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{});
310 continue;
311 }
312 // For a property like functional state the result will be
313 // calculated based on the true value of all conditions.
314 for (const auto& property : interface.second)
315 {
316 ipmi::sensor::PropertyMap props;
317 bool valid = false;
318 auto result = true;
319 for (const auto& value : std::get<OffsetValueMap>(property.second))
320 {
321 if (assertionSet.test(value.first))
322 {
323 // Skip update if skipOn is ASSERT
324 if (SkipAssertion::ASSERT == value.second.skip)
325 {
326 return IPMI_CC_OK;
327 }
328 result = result && std::get<bool>(value.second.assert);
329 valid = true;
330 }
331 else if (deassertionSet.test(value.first))
332 {
333 // Skip update if skipOn is DEASSERT
334 if (SkipAssertion::DEASSERT == value.second.skip)
335 {
336 return IPMI_CC_OK;
337 }
338 result = result && std::get<bool>(value.second.deassert);
339 valid = true;
340 }
341 }
342 for (const auto& value :
343 std::get<PreReqOffsetValueMap>(property.second))
344 {
345 if (assertionSet.test(value.first))
346 {
347 result = result && std::get<bool>(value.second.assert);
348 }
349 else if (deassertionSet.test(value.first))
350 {
351 result = result && std::get<bool>(value.second.deassert);
352 }
353 }
354 if (valid)
355 {
356 props.emplace(property.first, result);
357 interfaces.emplace(interface.first, std::move(props));
358 }
359 }
360 }
361
362 objects.emplace(sensorInfo.sensorPath, std::move(interfaces));
363 msg.append(std::move(objects));
364 return updateToDbus(msg);
365 }
366
367 } // namespace notify
368
369 namespace inventory
370 {
371
372 namespace get
373 {
374
375 #ifndef FEATURE_SENSORS_CACHE
376
assertion(const Info & sensorInfo)377 GetSensorResponse assertion(const Info& sensorInfo)
378 {
379 namespace fs = std::filesystem;
380
381 fs::path path{ipmi::sensor::inventoryRoot};
382 path += sensorInfo.sensorPath;
383
384 return ipmi::sensor::get::mapDbusToAssertion(
385 sensorInfo, path.string(),
386 sensorInfo.propertyInterfaces.begin()->first);
387 }
388
389 #else
390
391 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo,
392 const PropertyMap& /*properties*/)
393 {
394 // The assertion may contain multiple properties
395 // So we have to get the properties from DBus anyway
396 namespace fs = std::filesystem;
397
398 fs::path path{ipmi::sensor::inventoryRoot};
399 path += sensorInfo.sensorPath;
400
401 auto response = ipmi::sensor::get::mapDbusToAssertion(
402 sensorInfo, path.string(),
403 sensorInfo.propertyInterfaces.begin()->first);
404
405 if (!sensorCacheMap[id].has_value())
406 {
407 sensorCacheMap[id] = SensorData{};
408 }
409 sensorCacheMap[id]->response = response;
410 return response;
411 }
412
413 #endif
414
415 } // namespace get
416
417 } // namespace inventory
418 } // namespace sensor
419 } // namespace ipmi
420