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