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