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