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