xref: /openbmc/phosphor-host-ipmid/sensordatahandler.cpp (revision e6b2be523a942ae753a9f36a9f5c6bc80b24ca79)
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 
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 
34 ipmi::Cc 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::ccUnspecifiedError;
46     }
47     return ipmi::ccSuccess;
48 }
49 
50 namespace get
51 {
52 
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 
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& [intf, propertyMap] : interfaceList)
81     {
82         for (const auto& [property, values] : propertyMap)
83         {
84             try
85             {
86                 auto propValue =
87                     ipmi::getDbusProperty(bus, service, path, intf, property);
88 
89                 for (const auto& value : std::get<OffsetValueMap>(values))
90                 {
91                     if (propValue == value.second.assert)
92                     {
93                         setOffset(value.first, response);
94                         break;
95                     }
96                 }
97             }
98             catch (const std::exception& e)
99             {
100                 lg2::error(
101                     "mapDbusToAssertion: Failed to get property, service: {SERVICE},"
102                     " path: {PATH}, interface: {INTERFACE}, property name: {PRONAME}: {ERROR}",
103                     "SERVICE", service, "PATH", path, "INTERFACE", intf,
104                     "PRONAME", property, "ERROR", e);
105             }
106         }
107     }
108 
109     return response;
110 }
111 
112 GetSensorResponse mapDbusToEventdata2(const Info& sensorInfo)
113 {
114     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
115     GetSensorResponse response{};
116 
117     enableScanning(response);
118 
119     auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
120                                     sensorInfo.sensorPath);
121 
122     const auto& interfaceList = sensorInfo.propertyInterfaces;
123 
124     for (const auto& [intf, propertyMap] : interfaceList)
125     {
126         for (const auto& [property, values] : propertyMap)
127         {
128             try
129             {
130                 auto propValue = ipmi::getDbusProperty(
131                     bus, service, sensorInfo.sensorPath, intf, property);
132 
133                 for (const auto& value : std::get<OffsetValueMap>(values))
134                 {
135                     if (propValue == value.second.assert)
136                     {
137                         setReading(value.first, response);
138                         break;
139                     }
140                 }
141             }
142             catch (const std::exception& e)
143             {
144                 lg2::error(
145                     "mapDbusToEventdata2: Failed to get property, service: {SERVICE},"
146                     " path: {PATH}, interface: {INTERFACE}, property name: {PRONAME}: {ERROR}",
147                     "SERVICE", service, "PATH", sensorInfo.sensorPath,
148                     "INTERFACE", intf, "PRONAME", property, "ERROR", e);
149             }
150         }
151     }
152 
153     return response;
154 }
155 
156 #ifndef FEATURE_SENSORS_CACHE
157 GetSensorResponse assertion(const Info& sensorInfo)
158 {
159     return mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath,
160                               sensorInfo.sensorInterface);
161 }
162 
163 GetSensorResponse eventdata2(const Info& sensorInfo)
164 {
165     return mapDbusToEventdata2(sensorInfo);
166 }
167 #else
168 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo,
169                                            const PropertyMap& /*properties*/)
170 {
171     // The assertion may contain multiple properties
172     // So we have to get the properties from DBus anyway
173     auto response = mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath,
174                                        sensorInfo.sensorInterface);
175 
176     if (!sensorCacheMap[id].has_value())
177     {
178         sensorCacheMap[id] = SensorData{};
179     }
180     sensorCacheMap[id]->response = response;
181     return response;
182 }
183 
184 std::optional<GetSensorResponse> eventdata2(uint8_t id, const Info& sensorInfo,
185                                             const PropertyMap& /*properties*/)
186 {
187     // The eventdata2 may contain multiple properties
188     // So we have to get the properties from DBus anyway
189     auto response = mapDbusToEventdata2(sensorInfo);
190 
191     if (!sensorCacheMap[id].has_value())
192     {
193         sensorCacheMap[id] = SensorData{};
194     }
195     sensorCacheMap[id]->response = response;
196     return response;
197 }
198 
199 #endif // FEATURE_SENSORS_CACHE
200 
201 } // namespace get
202 
203 namespace set
204 {
205 
206 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
207                            const std::string& sensorPath,
208                            const std::string& command,
209                            const std::string& sensorInterface)
210 {
211     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
212     using namespace std::string_literals;
213 
214     auto dbusService = getService(bus, sensorInterface, sensorPath);
215 
216     return bus.new_method_call(dbusService.c_str(), sensorPath.c_str(),
217                                updateInterface.c_str(), command.c_str());
218 }
219 
220 ipmi::Cc eventdata(const SetSensorReadingReq&, const Info& sensorInfo,
221                    uint8_t data)
222 {
223     auto msg =
224         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
225                     "Set", sensorInfo.sensorInterface);
226 
227     const auto& interface = sensorInfo.propertyInterfaces.begin();
228     msg.append(interface->first);
229     for (const auto& property : interface->second)
230     {
231         msg.append(property.first);
232         const auto& iter = std::get<OffsetValueMap>(property.second).find(data);
233         if (iter == std::get<OffsetValueMap>(property.second).end())
234         {
235             lg2::error("Invalid event data");
236             return ipmi::ccParmOutOfRange;
237         }
238         msg.append(iter->second.assert);
239     }
240     return updateToDbus(msg);
241 }
242 
243 ipmi::Cc assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
244 {
245     std::bitset<16> assertionSet(getAssertionSet(cmdData).first);
246     std::bitset<16> deassertionSet(getAssertionSet(cmdData).second);
247     auto bothSet = assertionSet ^ deassertionSet;
248 
249     const auto& interface = sensorInfo.propertyInterfaces.begin();
250 
251     for (const auto& property : interface->second)
252     {
253         std::optional<Value> tmp;
254         for (const auto& value : std::get<OffsetValueMap>(property.second))
255         {
256             if (bothSet.size() <= value.first || !bothSet.test(value.first))
257             {
258                 // A BIOS shouldn't do this but ignore if they do.
259                 continue;
260             }
261 
262             if (assertionSet.test(value.first))
263             {
264                 tmp = value.second.assert;
265                 break;
266             }
267             if (deassertionSet.test(value.first))
268             {
269                 tmp = value.second.deassert;
270                 break;
271             }
272         }
273 
274         if (tmp)
275         {
276             auto msg = makeDbusMsg("org.freedesktop.DBus.Properties",
277                                    sensorInfo.sensorPath, "Set",
278                                    sensorInfo.sensorInterface);
279             msg.append(interface->first);
280             msg.append(property.first);
281             msg.append(*tmp);
282 
283             auto rc = updateToDbus(msg);
284             if (rc)
285             {
286                 return rc;
287             }
288         }
289     }
290 
291     return ipmi::ccSuccess;
292 }
293 
294 } // namespace set
295 
296 namespace notify
297 {
298 
299 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
300                            const std::string&, const std::string& command,
301                            const std::string&)
302 {
303     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
304     using namespace std::string_literals;
305 
306     static const auto dbusPath = "/xyz/openbmc_project/inventory"s;
307     std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath);
308 
309     return bus.new_method_call(dbusService.c_str(), dbusPath.c_str(),
310                                updateInterface.c_str(), command.c_str());
311 }
312 
313 ipmi::Cc assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
314 {
315     auto msg = makeDbusMsg(sensorInfo.sensorInterface, sensorInfo.sensorPath,
316                            "Notify", sensorInfo.sensorInterface);
317 
318     std::bitset<16> assertionSet(getAssertionSet(cmdData).first);
319     std::bitset<16> deassertionSet(getAssertionSet(cmdData).second);
320     ipmi::sensor::ObjectMap objects;
321     ipmi::sensor::InterfaceMap interfaces;
322     for (const auto& interface : sensorInfo.propertyInterfaces)
323     {
324         // An interface with no properties - It is possible that the sensor
325         // object on DBUS implements a DBUS interface with no properties.
326         // Make sure we add the interface to the list if interfaces on the
327         // object with an empty property map.
328         if (interface.second.empty())
329         {
330             interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{});
331             continue;
332         }
333         // For a property like functional state the result will be
334         // calculated based on the true value of all conditions.
335         for (const auto& property : interface.second)
336         {
337             ipmi::sensor::PropertyMap props;
338             bool valid = false;
339             auto result = true;
340             for (const auto& value : std::get<OffsetValueMap>(property.second))
341             {
342                 if (assertionSet.test(value.first))
343                 {
344                     // Skip update if skipOn is ASSERT
345                     if (SkipAssertion::ASSERT == value.second.skip)
346                     {
347                         return ipmi::ccSuccess;
348                     }
349                     result = result && std::get<bool>(value.second.assert);
350                     valid = true;
351                 }
352                 else if (deassertionSet.test(value.first))
353                 {
354                     // Skip update if skipOn is DEASSERT
355                     if (SkipAssertion::DEASSERT == value.second.skip)
356                     {
357                         return ipmi::ccSuccess;
358                     }
359                     result = result && std::get<bool>(value.second.deassert);
360                     valid = true;
361                 }
362             }
363             for (const auto& value :
364                  std::get<PreReqOffsetValueMap>(property.second))
365             {
366                 if (assertionSet.test(value.first))
367                 {
368                     result = result && std::get<bool>(value.second.assert);
369                 }
370                 else if (deassertionSet.test(value.first))
371                 {
372                     result = result && std::get<bool>(value.second.deassert);
373                 }
374             }
375             if (valid)
376             {
377                 props.emplace(property.first, result);
378                 interfaces.emplace(interface.first, std::move(props));
379             }
380         }
381     }
382 
383     objects.emplace(sensorInfo.sensorPath, std::move(interfaces));
384     msg.append(std::move(objects));
385     return updateToDbus(msg);
386 }
387 
388 } // namespace notify
389 
390 namespace inventory
391 {
392 
393 namespace get
394 {
395 
396 #ifndef FEATURE_SENSORS_CACHE
397 
398 GetSensorResponse assertion(const Info& sensorInfo)
399 {
400     namespace fs = std::filesystem;
401 
402     fs::path path{ipmi::sensor::inventoryRoot};
403     path += sensorInfo.sensorPath;
404 
405     return ipmi::sensor::get::mapDbusToAssertion(
406         sensorInfo, path.string(),
407         sensorInfo.propertyInterfaces.begin()->first);
408 }
409 
410 #else
411 
412 std::optional<GetSensorResponse> assertion(uint8_t id, const Info& sensorInfo,
413                                            const PropertyMap& /*properties*/)
414 {
415     // The assertion may contain multiple properties
416     // So we have to get the properties from DBus anyway
417     namespace fs = std::filesystem;
418 
419     fs::path path{ipmi::sensor::inventoryRoot};
420     path += sensorInfo.sensorPath;
421 
422     auto response = ipmi::sensor::get::mapDbusToAssertion(
423         sensorInfo, path.string(),
424         sensorInfo.propertyInterfaces.begin()->first);
425 
426     if (!sensorCacheMap[id].has_value())
427     {
428         sensorCacheMap[id] = SensorData{};
429     }
430     sensorCacheMap[id]->response = response;
431     return response;
432 }
433 
434 #endif
435 
436 } // namespace get
437 
438 } // namespace inventory
439 } // namespace sensor
440 } // namespace ipmi
441