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 SensorName nameParentLeaf(const Info& sensorInfo)
114 {
115     const auto pos = sensorInfo.sensorPath.find_last_of('/');
116     const auto leaf = sensorInfo.sensorPath.substr(pos + 1);
117 
118     const auto remaining = sensorInfo.sensorPath.substr(0, pos);
119 
120     const auto parentPos = remaining.find_last_of('/');
121     auto parent = remaining.substr(parentPos + 1);
122 
123     parent += "_" + leaf;
124     return parent;
125 }
126 
127 GetSensorResponse mapDbusToAssertion(const Info& sensorInfo,
128                                      const InstancePath& path,
129                                      const DbusInterface& interface)
130 {
131     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
132     GetSensorResponse response {};
133     auto responseData = reinterpret_cast<GetReadingResponse*>(response.data());
134 
135     auto service = ipmi::getService(bus, interface, path);
136 
137     const auto& interfaceList = sensorInfo.propertyInterfaces;
138 
139     for (const auto& interface : interfaceList)
140     {
141         for (const auto& property : interface.second)
142         {
143             auto propValue = ipmi::getDbusProperty(bus,
144                                                    service,
145                                                    path,
146                                                    interface.first,
147                                                    property.first);
148 
149             for (const auto& value : std::get<OffsetValueMap>(property.second))
150             {
151                 if (propValue == value.second.assert)
152                 {
153                     setOffset(value.first, responseData);
154                     break;
155                 }
156 
157             }
158         }
159     }
160 
161     return response;
162 }
163 
164 GetSensorResponse assertion(const Info& sensorInfo)
165 {
166     return mapDbusToAssertion(sensorInfo,
167                               sensorInfo.sensorPath,
168                               sensorInfo.sensorInterface);
169 }
170 
171 GetSensorResponse eventdata2(const Info& sensorInfo)
172 {
173     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
174     GetSensorResponse response {};
175     auto responseData = reinterpret_cast<GetReadingResponse*>(response.data());
176 
177     auto service = ipmi::getService(bus,
178                                     sensorInfo.sensorInterface,
179                                     sensorInfo.sensorPath);
180 
181     const auto& interfaceList = sensorInfo.propertyInterfaces;
182 
183     for (const auto& interface : interfaceList)
184     {
185         for (const auto& property : interface.second)
186         {
187             auto propValue = ipmi::getDbusProperty(bus,
188                                                    service,
189                                                    sensorInfo.sensorPath,
190                                                    interface.first,
191                                                    property.first);
192 
193             for (const auto& value : std::get<OffsetValueMap>(property.second))
194             {
195                 if (propValue == value.second.assert)
196                 {
197                     setReading(value.first, responseData);
198                     break;
199                 }
200             }
201         }
202     }
203 
204     return response;
205 }
206 
207 } //namespace get
208 
209 namespace set
210 {
211 
212 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
213                            const std::string& sensorPath,
214                            const std::string& command,
215                            const std::string& sensorInterface)
216 {
217     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
218     using namespace std::string_literals;
219 
220     auto dbusService = getService(bus,
221                                   sensorInterface,
222                                   sensorPath);
223 
224     return bus.new_method_call(dbusService.c_str(),
225                                sensorPath.c_str(),
226                                updateInterface.c_str(),
227                                command.c_str());
228 }
229 
230 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData,
231                      const Info& sensorInfo,
232                      uint8_t data)
233 {
234     auto msg = makeDbusMsg(
235                    "org.freedesktop.DBus.Properties",
236                    sensorInfo.sensorPath,
237                    "Set",
238                    sensorInfo.sensorInterface);
239 
240     const auto& interface = sensorInfo.propertyInterfaces.begin();
241     msg.append(interface->first);
242     for (const auto& property : interface->second)
243     {
244         msg.append(property.first);
245         const auto& iter = std::get<OffsetValueMap>(property.second).find(data);
246         if (iter == std::get<OffsetValueMap>(property.second).end())
247         {
248             log<level::ERR>("Invalid event data");
249             return IPMI_CC_PARM_OUT_OF_RANGE;
250         }
251         msg.append(iter->second.assert);
252     }
253     return updateToDbus(msg);
254 }
255 
256 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
257                      const Info& sensorInfo)
258 {
259     std::bitset<16> assertionSet(getAssertionSet(cmdData).first);
260     std::bitset<16> deassertionSet(getAssertionSet(cmdData).second);
261     auto bothSet = assertionSet ^ deassertionSet;
262 
263     const auto& interface = sensorInfo.propertyInterfaces.begin();
264 
265     for (const auto& property : interface->second)
266     {
267         Value tmp{mapbox::util::no_init()};
268         for (const auto& value : std::get<OffsetValueMap>(property.second))
269         {
270             if (bothSet.size() <= value.first || !bothSet.test(value.first))
271             {
272                 // A BIOS shouldn't do this but ignore if they do.
273                 continue;
274             }
275 
276             if (assertionSet.test(value.first))
277             {
278                 tmp = value.second.assert;
279                 break;
280             }
281             if (deassertionSet.test(value.first))
282             {
283                 tmp = value.second.deassert;
284                 break;
285             }
286         }
287 
288         if (tmp.valid())
289         {
290             auto msg = makeDbusMsg(
291                    "org.freedesktop.DBus.Properties",
292                    sensorInfo.sensorPath,
293                    "Set",
294                    sensorInfo.sensorInterface);
295             msg.append(interface->first);
296             msg.append(property.first);
297             msg.append(tmp);
298 
299             auto rc = updateToDbus(msg);
300             if (rc)
301             {
302                 return rc;
303             }
304         }
305     }
306 
307     return IPMI_CC_OK;
308 }
309 
310 }//namespace set
311 
312 namespace notify
313 {
314 
315 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
316                            const std::string& sensorPath,
317                            const std::string& command,
318                            const std::string& sensorInterface)
319 {
320     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
321     using namespace std::string_literals;
322 
323     static const auto dbusPath = "/xyz/openbmc_project/inventory"s;
324     std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath);
325 
326     return bus.new_method_call(dbusService.c_str(),
327                                dbusPath.c_str(),
328                                updateInterface.c_str(),
329                                command.c_str());
330 }
331 
332 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData,
333                      const Info& sensorInfo)
334 {
335     auto msg = makeDbusMsg(
336                    sensorInfo.sensorInterface,
337                    sensorInfo.sensorPath,
338                    "Notify",
339                    sensorInfo.sensorInterface);
340 
341     std::bitset<16> assertionSet(getAssertionSet(cmdData).first);
342     std::bitset<16> deassertionSet(getAssertionSet(cmdData).second);
343     ipmi::sensor::ObjectMap objects;
344     ipmi::sensor::InterfaceMap interfaces;
345     for (const auto& interface : sensorInfo.propertyInterfaces)
346     {
347         //For a property like functional state the result will be
348         //calculated based on the true value of all conditions.
349         for (const auto& property : interface.second)
350         {
351             ipmi::sensor::PropertyMap props;
352             bool valid = false;
353             auto result = true;
354             for (const auto& value : std::get<OffsetValueMap>(property.second))
355             {
356                 if (assertionSet.test(value.first))
357                 {
358                     //Skip update if skipOn is ASSERT
359                     if (SkipAssertion::ASSERT == value.second.skip)
360                     {
361                         return IPMI_CC_OK;
362                     }
363                     result = result && value.second.assert.get<bool>();
364                     valid = true;
365                 }
366                 else if (deassertionSet.test(value.first))
367                 {
368                     //Skip update if skipOn is DEASSERT
369                     if (SkipAssertion::DEASSERT == value.second.skip)
370                     {
371                         return IPMI_CC_OK;
372                     }
373                     result = result && value.second.deassert.get<bool>();
374                     valid = true;
375                 }
376             }
377             for (const auto& value :
378                     std::get<PreReqOffsetValueMap>(property.second))
379             {
380                 if (assertionSet.test(value.first))
381                 {
382                     result = result && value.second.assert.get<bool>();
383                 }
384                 else if (deassertionSet.test(value.first))
385                 {
386                     result = result && value.second.deassert.get<bool>();
387                 }
388             }
389             if (valid)
390             {
391                 props.emplace(property.first, result);
392                 interfaces.emplace(interface.first, std::move(props));
393             }
394         }
395     }
396 
397     objects.emplace(sensorInfo.sensorPath, std::move(interfaces));
398     msg.append(std::move(objects));
399     return updateToDbus(msg);
400 }
401 
402 }//namespace notify
403 
404 namespace inventory
405 {
406 
407 namespace get
408 {
409 
410 GetSensorResponse assertion(const Info& sensorInfo)
411 {
412     namespace fs = std::experimental::filesystem;
413 
414     fs::path path{ipmi::sensor::inventoryRoot};
415     path += sensorInfo.sensorPath;
416 
417     return ipmi::sensor::get::mapDbusToAssertion(
418             sensorInfo,
419             path.string(),
420             sensorInfo.propertyInterfaces.begin()->first);
421 }
422 
423 } //namespace get
424 
425 } // namespace inventory
426 }//namespace sensor
427 }//namespace ipmi
428