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 <sdbusplus/message/types.hpp>
11 #include <xyz/openbmc_project/Common/error.hpp>
12 
13 namespace ipmi
14 {
15 namespace sensor
16 {
17 
18 using namespace phosphor::logging;
19 using InternalFailure =
20     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
21 
22 static constexpr auto MAPPER_BUSNAME = "xyz.openbmc_project.ObjectMapper";
23 static constexpr auto MAPPER_PATH = "/xyz/openbmc_project/object_mapper";
24 static constexpr auto MAPPER_INTERFACE = "xyz.openbmc_project.ObjectMapper";
25 
26 /** @brief get the D-Bus service and service path
27  *  @param[in] bus - The Dbus bus object
28  *  @param[in] interface - interface to the service
29  *  @param[in] path - interested path in the list of objects
30  *  @return pair of service path and service
31  */
32 ServicePath getServiceAndPath(sdbusplus::bus::bus& bus,
33                               const std::string& interface,
34                               const std::string& path)
35 {
36     auto depth = 0;
37     auto mapperCall = bus.new_method_call(MAPPER_BUSNAME, MAPPER_PATH,
38                                           MAPPER_INTERFACE, "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.c_str()),
48                         entry("INTERFACE=%s", interface.c_str()));
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.c_str()),
58                         entry("INTERFACE=%s", interface.c_str()));
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>("Couldn't find D-Bus path",
72                         entry("PATH=%s", path.c_str()),
73                         entry("INTERFACE=%s", interface.c_str()));
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 
134     enableScanning(&response);
135 
136     auto service = ipmi::getService(bus, interface, path);
137 
138     const auto& interfaceList = sensorInfo.propertyInterfaces;
139 
140     for (const auto& interface : interfaceList)
141     {
142         for (const auto& property : interface.second)
143         {
144             auto propValue = ipmi::getDbusProperty(
145                 bus, service, path, interface.first, property.first);
146 
147             for (const auto& value : std::get<OffsetValueMap>(property.second))
148             {
149                 if (propValue == value.second.assert)
150                 {
151                     setOffset(value.first, &response);
152                     break;
153                 }
154             }
155         }
156     }
157 
158     return response;
159 }
160 
161 GetSensorResponse assertion(const Info& sensorInfo)
162 {
163     return mapDbusToAssertion(sensorInfo, sensorInfo.sensorPath,
164                               sensorInfo.sensorInterface);
165 }
166 
167 GetSensorResponse eventdata2(const Info& sensorInfo)
168 {
169     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
170     GetSensorResponse response{};
171 
172     enableScanning(&response);
173 
174     auto service = ipmi::getService(bus, sensorInfo.sensorInterface,
175                                     sensorInfo.sensorPath);
176 
177     const auto& interfaceList = sensorInfo.propertyInterfaces;
178 
179     for (const auto& interface : interfaceList)
180     {
181         for (const auto& property : interface.second)
182         {
183             auto propValue =
184                 ipmi::getDbusProperty(bus, service, sensorInfo.sensorPath,
185                                       interface.first, property.first);
186 
187             for (const auto& value : std::get<OffsetValueMap>(property.second))
188             {
189                 if (propValue == value.second.assert)
190                 {
191                     setReading(value.first, &response);
192                     break;
193                 }
194             }
195         }
196     }
197 
198     return response;
199 }
200 
201 } // namespace get
202 
203 namespace set
204 {
205 
206 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
207                            const std::string& sensorPath,
208                            const std::string& command,
209                            const std::string& sensorInterface)
210 {
211     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
212     using namespace std::string_literals;
213 
214     auto dbusService = getService(bus, sensorInterface, sensorPath);
215 
216     return bus.new_method_call(dbusService.c_str(), sensorPath.c_str(),
217                                updateInterface.c_str(), command.c_str());
218 }
219 
220 ipmi_ret_t eventdata(const SetSensorReadingReq& cmdData, const Info& sensorInfo,
221                      uint8_t data)
222 {
223     auto msg =
224         makeDbusMsg("org.freedesktop.DBus.Properties", sensorInfo.sensorPath,
225                     "Set", sensorInfo.sensorInterface);
226 
227     const auto& interface = sensorInfo.propertyInterfaces.begin();
228     msg.append(interface->first);
229     for (const auto& property : interface->second)
230     {
231         msg.append(property.first);
232         const auto& iter = std::get<OffsetValueMap>(property.second).find(data);
233         if (iter == std::get<OffsetValueMap>(property.second).end())
234         {
235             log<level::ERR>("Invalid event data");
236             return IPMI_CC_PARM_OUT_OF_RANGE;
237         }
238         msg.append(iter->second.assert);
239     }
240     return updateToDbus(msg);
241 }
242 
243 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
244 {
245     std::bitset<16> assertionSet(getAssertionSet(cmdData).first);
246     std::bitset<16> deassertionSet(getAssertionSet(cmdData).second);
247     auto bothSet = assertionSet ^ deassertionSet;
248 
249     const auto& interface = sensorInfo.propertyInterfaces.begin();
250 
251     for (const auto& property : interface->second)
252     {
253         std::optional<Value> tmp;
254         for (const auto& value : std::get<OffsetValueMap>(property.second))
255         {
256             if (bothSet.size() <= value.first || !bothSet.test(value.first))
257             {
258                 // A BIOS shouldn't do this but ignore if they do.
259                 continue;
260             }
261 
262             if (assertionSet.test(value.first))
263             {
264                 tmp = value.second.assert;
265                 break;
266             }
267             if (deassertionSet.test(value.first))
268             {
269                 tmp = value.second.deassert;
270                 break;
271             }
272         }
273 
274         if (tmp)
275         {
276             auto msg = makeDbusMsg("org.freedesktop.DBus.Properties",
277                                    sensorInfo.sensorPath, "Set",
278                                    sensorInfo.sensorInterface);
279             msg.append(interface->first);
280             msg.append(property.first);
281             msg.append(*tmp);
282 
283             auto rc = updateToDbus(msg);
284             if (rc)
285             {
286                 return rc;
287             }
288         }
289     }
290 
291     return IPMI_CC_OK;
292 }
293 
294 } // namespace set
295 
296 namespace notify
297 {
298 
299 IpmiUpdateData makeDbusMsg(const std::string& updateInterface,
300                            const std::string& sensorPath,
301                            const std::string& command,
302                            const std::string& sensorInterface)
303 {
304     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
305     using namespace std::string_literals;
306 
307     static const auto dbusPath = "/xyz/openbmc_project/inventory"s;
308     std::string dbusService = ipmi::getService(bus, updateInterface, dbusPath);
309 
310     return bus.new_method_call(dbusService.c_str(), dbusPath.c_str(),
311                                updateInterface.c_str(), command.c_str());
312 }
313 
314 ipmi_ret_t assertion(const SetSensorReadingReq& cmdData, const Info& sensorInfo)
315 {
316     auto msg = makeDbusMsg(sensorInfo.sensorInterface, sensorInfo.sensorPath,
317                            "Notify", sensorInfo.sensorInterface);
318 
319     std::bitset<16> assertionSet(getAssertionSet(cmdData).first);
320     std::bitset<16> deassertionSet(getAssertionSet(cmdData).second);
321     ipmi::sensor::ObjectMap objects;
322     ipmi::sensor::InterfaceMap interfaces;
323     for (const auto& interface : sensorInfo.propertyInterfaces)
324     {
325         // An interface with no properties - It is possible that the sensor
326         // object on DBUS implements a DBUS interface with no properties.
327         // Make sure we add the interface to the list if interfaces on the
328         // object with an empty property map.
329         if (interface.second.empty())
330         {
331             interfaces.emplace(interface.first, ipmi::sensor::PropertyMap{});
332             continue;
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 && std::get<bool>(value.second.assert);
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 && std::get<bool>(value.second.deassert);
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 && std::get<bool>(value.second.assert);
370                 }
371                 else if (deassertionSet.test(value.first))
372                 {
373                     result = result && std::get<bool>(value.second.deassert);
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