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