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