xref: /openbmc/phosphor-host-ipmid/dbus-sdr/sdrutils.cpp (revision 2b42d7ee5a6f119fae969200a9ee8597b2b67da7)
1 /*
2 // Copyright (c) 2018 Intel Corporation
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 //      http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 */
16 
17 #include "dbus-sdr/sdrutils.hpp"
18 
19 namespace details
20 {
21 uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
22 {
23     static std::shared_ptr<SensorSubTree> sensorTreePtr;
24     static uint16_t sensorUpdatedIndex = 0;
25     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
26     static sdbusplus::bus::match::match sensorAdded(
27         *dbus,
28         "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
29         "sensors/'",
30         [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
31 
32     static sdbusplus::bus::match::match sensorRemoved(
33         *dbus,
34         "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
35         "openbmc_project/sensors/'",
36         [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
37 
38     if (sensorTreePtr)
39     {
40         subtree = sensorTreePtr;
41         return sensorUpdatedIndex;
42     }
43 
44     sensorTreePtr = std::make_shared<SensorSubTree>();
45 
46     static constexpr const int32_t depth = 2;
47 
48     auto lbdUpdateSensorTree = [&dbus](const char* path,
49                                        const auto& interfaces) {
50         auto mapperCall = dbus->new_method_call(
51             "xyz.openbmc_project.ObjectMapper",
52             "/xyz/openbmc_project/object_mapper",
53             "xyz.openbmc_project.ObjectMapper", "GetSubTree");
54         SensorSubTree sensorTreePartial;
55 
56         mapperCall.append(path, depth, interfaces);
57 
58         try
59         {
60             auto mapperReply = dbus->call(mapperCall);
61             mapperReply.read(sensorTreePartial);
62         }
63         catch (sdbusplus::exception_t& e)
64         {
65             phosphor::logging::log<phosphor::logging::level::ERR>(
66                 "fail to update subtree",
67                 phosphor::logging::entry("PATH=%s", path),
68                 phosphor::logging::entry("WHAT=%s", e.what()));
69             return false;
70         }
71         if constexpr (debug)
72         {
73             std::fprintf(stderr, "IPMI updated: %zu sensors under %s\n",
74                          sensorTreePartial.size(), path);
75         }
76         sensorTreePtr->merge(std::move(sensorTreePartial));
77         return true;
78     };
79 
80     // Add sensors to SensorTree
81     static constexpr const std::array sensorInterfaces = {
82         "xyz.openbmc_project.Sensor.Value",
83         "xyz.openbmc_project.Sensor.Threshold.Warning",
84         "xyz.openbmc_project.Sensor.Threshold.Critical"};
85     static constexpr const std::array vrInterfaces = {
86         "xyz.openbmc_project.Control.VoltageRegulatorMode"};
87 
88     bool sensorRez =
89         lbdUpdateSensorTree("/xyz/openbmc_project/sensors", sensorInterfaces);
90 
91     // Error if searching for sensors failed.
92     if (!sensorRez)
93     {
94         return sensorUpdatedIndex;
95     }
96 
97     // Add VR control as optional search path.
98     (void)lbdUpdateSensorTree("/xyz/openbmc_project/vr", vrInterfaces);
99 
100     subtree = sensorTreePtr;
101     sensorUpdatedIndex++;
102     // The SDR is being regenerated, wipe the old stats
103     sdrStatsTable.wipeTable();
104     return sensorUpdatedIndex;
105 }
106 
107 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
108 {
109     static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
110     bool sensorNumMapUpated = false;
111     static uint16_t prevSensorUpdatedIndex = 0;
112     std::shared_ptr<SensorSubTree> sensorTree;
113     uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
114     if (!sensorTree)
115     {
116         return sensorNumMapUpated;
117     }
118 
119     if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
120     {
121         sensorNumMap = sensorNumMapPtr;
122         return sensorNumMapUpated;
123     }
124     prevSensorUpdatedIndex = curSensorUpdatedIndex;
125 
126     sensorNumMapPtr = std::make_shared<SensorNumMap>();
127 
128     uint16_t sensorNum = 0;
129     uint16_t sensorIndex = 0;
130     for (const auto& sensor : *sensorTree)
131     {
132         sensorNumMapPtr->insert(
133             SensorNumMap::value_type(sensorNum, sensor.first));
134         sensorIndex++;
135         if (sensorIndex == maxSensorsPerLUN)
136         {
137             sensorIndex = lun1Sensor0;
138         }
139         else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
140         {
141             // Skip assigning LUN 0x2 any sensors
142             sensorIndex = lun3Sensor0;
143         }
144         else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
145         {
146             // this is an error, too many IPMI sensors
147             throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
148         }
149         sensorNum = sensorIndex;
150     }
151     sensorNumMap = sensorNumMapPtr;
152     sensorNumMapUpated = true;
153     return sensorNumMapUpated;
154 }
155 } // namespace details
156 
157 bool getSensorSubtree(SensorSubTree& subtree)
158 {
159     std::shared_ptr<SensorSubTree> sensorTree;
160     details::getSensorSubtree(sensorTree);
161     if (!sensorTree)
162     {
163         return false;
164     }
165 
166     subtree = *sensorTree;
167     return true;
168 }
169 
170 std::string getSensorTypeStringFromPath(const std::string& path)
171 {
172     // get sensor type string from path, path is defined as
173     // /xyz/openbmc_project/sensors/<type>/label
174     size_t typeEnd = path.rfind("/");
175     if (typeEnd == std::string::npos)
176     {
177         return path;
178     }
179     size_t typeStart = path.rfind("/", typeEnd - 1);
180     if (typeStart == std::string::npos)
181     {
182         return path;
183     }
184     // Start at the character after the '/'
185     typeStart++;
186     return path.substr(typeStart, typeEnd - typeStart);
187 }
188 
189 uint8_t getSensorTypeFromPath(const std::string& path)
190 {
191     uint8_t sensorType = 0;
192     std::string type = getSensorTypeStringFromPath(path);
193     auto findSensor = sensorTypes.find(type.c_str());
194     if (findSensor != sensorTypes.end())
195     {
196         sensorType =
197             static_cast<uint8_t>(std::get<sensorTypeCodes>(findSensor->second));
198     } // else default 0x0 RESERVED
199 
200     return sensorType;
201 }
202 
203 uint16_t getSensorNumberFromPath(const std::string& path)
204 {
205     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
206     details::getSensorNumMap(sensorNumMapPtr);
207     if (!sensorNumMapPtr)
208     {
209         return invalidSensorNumber;
210     }
211 
212     try
213     {
214         return sensorNumMapPtr->right.at(path);
215     }
216     catch (std::out_of_range& e)
217     {
218         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
219         return invalidSensorNumber;
220     }
221 }
222 
223 uint8_t getSensorEventTypeFromPath(const std::string& path)
224 {
225     uint8_t sensorEventType = 0;
226     std::string type = getSensorTypeStringFromPath(path);
227     auto findSensor = sensorTypes.find(type.c_str());
228     if (findSensor != sensorTypes.end())
229     {
230         sensorEventType = static_cast<uint8_t>(
231             std::get<sensorEventTypeCodes>(findSensor->second));
232     }
233 
234     return sensorEventType;
235 }
236 
237 std::string getPathFromSensorNumber(uint16_t sensorNum)
238 {
239     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
240     details::getSensorNumMap(sensorNumMapPtr);
241     if (!sensorNumMapPtr)
242     {
243         return std::string();
244     }
245 
246     try
247     {
248         return sensorNumMapPtr->left.at(sensorNum);
249     }
250     catch (std::out_of_range& e)
251     {
252         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
253         return std::string();
254     }
255 }
256 
257 namespace ipmi
258 {
259 
260 std::map<std::string, std::vector<std::string>>
261     getObjectInterfaces(const char* path)
262 {
263     std::map<std::string, std::vector<std::string>> interfacesResponse;
264     std::vector<std::string> interfaces;
265     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
266 
267     sdbusplus::message::message getObjectMessage =
268         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
269                               "/xyz/openbmc_project/object_mapper",
270                               "xyz.openbmc_project.ObjectMapper", "GetObject");
271     getObjectMessage.append(path, interfaces);
272 
273     try
274     {
275         sdbusplus::message::message response = dbus->call(getObjectMessage);
276         response.read(interfacesResponse);
277     }
278     catch (const std::exception& e)
279     {
280         phosphor::logging::log<phosphor::logging::level::ERR>(
281             "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
282             phosphor::logging::entry("WHAT=%s", e.what()));
283     }
284 
285     return interfacesResponse;
286 }
287 
288 std::map<std::string, Value> getEntityManagerProperties(const char* path,
289                                                         const char* interface)
290 {
291     std::map<std::string, Value> properties;
292     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
293 
294     sdbusplus::message::message getProperties =
295         dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
296                               "org.freedesktop.DBus.Properties", "GetAll");
297     getProperties.append(interface);
298 
299     try
300     {
301         sdbusplus::message::message response = dbus->call(getProperties);
302         response.read(properties);
303     }
304     catch (const std::exception& e)
305     {
306         phosphor::logging::log<phosphor::logging::level::ERR>(
307             "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
308             phosphor::logging::entry("INTF=%s", interface),
309             phosphor::logging::entry("WHAT=%s", e.what()));
310     }
311 
312     return properties;
313 }
314 
315 const std::string* getSensorConfigurationInterface(
316     const std::map<std::string, std::vector<std::string>>&
317         sensorInterfacesResponse)
318 {
319     auto entityManagerService =
320         sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
321     if (entityManagerService == sensorInterfacesResponse.end())
322     {
323         return nullptr;
324     }
325 
326     // Find the fan configuration first (fans can have multiple configuration
327     // interfaces).
328     for (const auto& entry : entityManagerService->second)
329     {
330         if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
331             entry == "xyz.openbmc_project.Configuration.I2CFan" ||
332             entry == "xyz.openbmc_project.Configuration.NuvotonFan")
333         {
334             return &entry;
335         }
336     }
337 
338     for (const auto& entry : entityManagerService->second)
339     {
340         if (boost::algorithm::starts_with(entry,
341                                           "xyz.openbmc_project.Configuration."))
342         {
343             return &entry;
344         }
345     }
346 
347     return nullptr;
348 }
349 
350 // Follow Association properties for Sensor back to the Board dbus object to
351 // check for an EntityId and EntityInstance property.
352 void updateIpmiFromAssociation(const std::string& path,
353                                const DbusInterfaceMap& sensorMap,
354                                uint8_t& entityId, uint8_t& entityInstance)
355 {
356     namespace fs = std::filesystem;
357 
358     auto sensorAssociationObject =
359         sensorMap.find("xyz.openbmc_project.Association.Definitions");
360     if (sensorAssociationObject == sensorMap.end())
361     {
362         if constexpr (debug)
363         {
364             std::fprintf(stderr, "path=%s, no association interface found\n",
365                          path.c_str());
366         }
367 
368         return;
369     }
370 
371     auto associationObject =
372         sensorAssociationObject->second.find("Associations");
373     if (associationObject == sensorAssociationObject->second.end())
374     {
375         if constexpr (debug)
376         {
377             std::fprintf(stderr, "path=%s, no association records found\n",
378                          path.c_str());
379         }
380 
381         return;
382     }
383 
384     std::vector<Association> associationValues =
385         std::get<std::vector<Association>>(associationObject->second);
386 
387     // loop through the Associations looking for the right one:
388     for (const auto& entry : associationValues)
389     {
390         // forward, reverse, endpoint
391         const std::string& forward = std::get<0>(entry);
392         const std::string& reverse = std::get<1>(entry);
393         const std::string& endpoint = std::get<2>(entry);
394 
395         // We only currently concern ourselves with chassis+all_sensors.
396         if (!(forward == "chassis" && reverse == "all_sensors"))
397         {
398             continue;
399         }
400 
401         // the endpoint is the board entry provided by
402         // Entity-Manager. so let's grab its properties if it has
403         // the right interface.
404 
405         // just try grabbing the properties first.
406         std::map<std::string, Value> ipmiProperties =
407             getEntityManagerProperties(
408                 endpoint.c_str(),
409                 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
410 
411         auto entityIdProp = ipmiProperties.find("EntityId");
412         auto entityInstanceProp = ipmiProperties.find("EntityInstance");
413         if (entityIdProp != ipmiProperties.end())
414         {
415             entityId =
416                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
417         }
418         if (entityInstanceProp != ipmiProperties.end())
419         {
420             entityInstance = static_cast<uint8_t>(
421                 std::get<uint64_t>(entityInstanceProp->second));
422         }
423 
424         // Now check the entity-manager entry for this sensor to see
425         // if it has its own value and use that instead.
426         //
427         // In theory, checking this first saves us from checking
428         // both, except in most use-cases identified, there won't be
429         // a per sensor override, so we need to always check both.
430         std::string sensorNameFromPath = fs::path(path).filename();
431 
432         std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
433 
434         // Download the interfaces for the sensor from
435         // Entity-Manager to find the name of the configuration
436         // interface.
437         std::map<std::string, std::vector<std::string>>
438             sensorInterfacesResponse =
439                 getObjectInterfaces(sensorConfigPath.c_str());
440 
441         const std::string* configurationInterface =
442             getSensorConfigurationInterface(sensorInterfacesResponse);
443 
444         // We didnt' find a configuration interface for this sensor, but we
445         // followed the Association property to get here, so we're done
446         // searching.
447         if (!configurationInterface)
448         {
449             break;
450         }
451 
452         // We found a configuration interface.
453         std::map<std::string, Value> configurationProperties =
454             getEntityManagerProperties(sensorConfigPath.c_str(),
455                                        configurationInterface->c_str());
456 
457         entityIdProp = configurationProperties.find("EntityId");
458         entityInstanceProp = configurationProperties.find("EntityInstance");
459         if (entityIdProp != configurationProperties.end())
460         {
461             entityId =
462                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
463         }
464         if (entityInstanceProp != configurationProperties.end())
465         {
466             entityInstance = static_cast<uint8_t>(
467                 std::get<uint64_t>(entityInstanceProp->second));
468         }
469 
470         // stop searching Association records.
471         break;
472     } // end for Association vectors.
473 
474     if constexpr (debug)
475     {
476         std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
477                      path.c_str(), entityId, entityInstance);
478     }
479 }
480 
481 } // namespace ipmi
482