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