xref: /openbmc/phosphor-host-ipmid/dbus-sdr/sdrutils.cpp (revision 9a5b51e3dea5d52861ce7b2c18ca826b7b2bca4c)
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 = static_cast<uint8_t>(findSensor->second);
197     } // else default 0x0 RESERVED
198 
199     return sensorType;
200 }
201 
202 uint16_t getSensorNumberFromPath(const std::string& path)
203 {
204     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
205     details::getSensorNumMap(sensorNumMapPtr);
206     if (!sensorNumMapPtr)
207     {
208         return invalidSensorNumber;
209     }
210 
211     try
212     {
213         return sensorNumMapPtr->right.at(path);
214     }
215     catch (std::out_of_range& e)
216     {
217         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
218         return invalidSensorNumber;
219     }
220 }
221 
222 uint8_t getSensorEventTypeFromPath(const std::string& path)
223 {
224     // TODO: Add support for additional reading types as needed
225     return 0x1; // reading type = threshold
226 }
227 
228 std::string getPathFromSensorNumber(uint16_t sensorNum)
229 {
230     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
231     details::getSensorNumMap(sensorNumMapPtr);
232     if (!sensorNumMapPtr)
233     {
234         return std::string();
235     }
236 
237     try
238     {
239         return sensorNumMapPtr->left.at(sensorNum);
240     }
241     catch (std::out_of_range& e)
242     {
243         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
244         return std::string();
245     }
246 }
247 
248 namespace ipmi
249 {
250 
251 std::map<std::string, std::vector<std::string>>
252     getObjectInterfaces(const char* path)
253 {
254     std::map<std::string, std::vector<std::string>> interfacesResponse;
255     std::vector<std::string> interfaces;
256     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
257 
258     sdbusplus::message::message getObjectMessage =
259         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
260                               "/xyz/openbmc_project/object_mapper",
261                               "xyz.openbmc_project.ObjectMapper", "GetObject");
262     getObjectMessage.append(path, interfaces);
263 
264     try
265     {
266         sdbusplus::message::message response = dbus->call(getObjectMessage);
267         response.read(interfacesResponse);
268     }
269     catch (const std::exception& e)
270     {
271         phosphor::logging::log<phosphor::logging::level::ERR>(
272             "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
273             phosphor::logging::entry("WHAT=%s", e.what()));
274     }
275 
276     return interfacesResponse;
277 }
278 
279 std::map<std::string, Value> getEntityManagerProperties(const char* path,
280                                                         const char* interface)
281 {
282     std::map<std::string, Value> properties;
283     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
284 
285     sdbusplus::message::message getProperties =
286         dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
287                               "org.freedesktop.DBus.Properties", "GetAll");
288     getProperties.append(interface);
289 
290     try
291     {
292         sdbusplus::message::message response = dbus->call(getProperties);
293         response.read(properties);
294     }
295     catch (const std::exception& e)
296     {
297         phosphor::logging::log<phosphor::logging::level::ERR>(
298             "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
299             phosphor::logging::entry("INTF=%s", interface),
300             phosphor::logging::entry("WHAT=%s", e.what()));
301     }
302 
303     return properties;
304 }
305 
306 const std::string* getSensorConfigurationInterface(
307     const std::map<std::string, std::vector<std::string>>&
308         sensorInterfacesResponse)
309 {
310     auto entityManagerService =
311         sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
312     if (entityManagerService == sensorInterfacesResponse.end())
313     {
314         return nullptr;
315     }
316 
317     // Find the fan configuration first (fans can have multiple configuration
318     // interfaces).
319     for (const auto& entry : entityManagerService->second)
320     {
321         if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
322             entry == "xyz.openbmc_project.Configuration.I2CFan" ||
323             entry == "xyz.openbmc_project.Configuration.NuvotonFan")
324         {
325             return &entry;
326         }
327     }
328 
329     for (const auto& entry : entityManagerService->second)
330     {
331         if (boost::algorithm::starts_with(entry,
332                                           "xyz.openbmc_project.Configuration."))
333         {
334             return &entry;
335         }
336     }
337 
338     return nullptr;
339 }
340 
341 // Follow Association properties for Sensor back to the Board dbus object to
342 // check for an EntityId and EntityInstance property.
343 void updateIpmiFromAssociation(const std::string& path,
344                                const DbusInterfaceMap& sensorMap,
345                                uint8_t& entityId, uint8_t& entityInstance)
346 {
347     namespace fs = std::filesystem;
348 
349     auto sensorAssociationObject =
350         sensorMap.find("xyz.openbmc_project.Association.Definitions");
351     if (sensorAssociationObject == sensorMap.end())
352     {
353         if constexpr (debug)
354         {
355             std::fprintf(stderr, "path=%s, no association interface found\n",
356                          path.c_str());
357         }
358 
359         return;
360     }
361 
362     auto associationObject =
363         sensorAssociationObject->second.find("Associations");
364     if (associationObject == sensorAssociationObject->second.end())
365     {
366         if constexpr (debug)
367         {
368             std::fprintf(stderr, "path=%s, no association records found\n",
369                          path.c_str());
370         }
371 
372         return;
373     }
374 
375     std::vector<Association> associationValues =
376         std::get<std::vector<Association>>(associationObject->second);
377 
378     // loop through the Associations looking for the right one:
379     for (const auto& entry : associationValues)
380     {
381         // forward, reverse, endpoint
382         const std::string& forward = std::get<0>(entry);
383         const std::string& reverse = std::get<1>(entry);
384         const std::string& endpoint = std::get<2>(entry);
385 
386         // We only currently concern ourselves with chassis+all_sensors.
387         if (!(forward == "chassis" && reverse == "all_sensors"))
388         {
389             continue;
390         }
391 
392         // the endpoint is the board entry provided by
393         // Entity-Manager. so let's grab its properties if it has
394         // the right interface.
395 
396         // just try grabbing the properties first.
397         std::map<std::string, Value> ipmiProperties =
398             getEntityManagerProperties(
399                 endpoint.c_str(),
400                 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
401 
402         auto entityIdProp = ipmiProperties.find("EntityId");
403         auto entityInstanceProp = ipmiProperties.find("EntityInstance");
404         if (entityIdProp != ipmiProperties.end())
405         {
406             entityId =
407                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
408         }
409         if (entityInstanceProp != ipmiProperties.end())
410         {
411             entityInstance = static_cast<uint8_t>(
412                 std::get<uint64_t>(entityInstanceProp->second));
413         }
414 
415         // Now check the entity-manager entry for this sensor to see
416         // if it has its own value and use that instead.
417         //
418         // In theory, checking this first saves us from checking
419         // both, except in most use-cases identified, there won't be
420         // a per sensor override, so we need to always check both.
421         std::string sensorNameFromPath = fs::path(path).filename();
422 
423         std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
424 
425         // Download the interfaces for the sensor from
426         // Entity-Manager to find the name of the configuration
427         // interface.
428         std::map<std::string, std::vector<std::string>>
429             sensorInterfacesResponse =
430                 getObjectInterfaces(sensorConfigPath.c_str());
431 
432         const std::string* configurationInterface =
433             getSensorConfigurationInterface(sensorInterfacesResponse);
434 
435         // We didnt' find a configuration interface for this sensor, but we
436         // followed the Association property to get here, so we're done
437         // searching.
438         if (!configurationInterface)
439         {
440             break;
441         }
442 
443         // We found a configuration interface.
444         std::map<std::string, Value> configurationProperties =
445             getEntityManagerProperties(sensorConfigPath.c_str(),
446                                        configurationInterface->c_str());
447 
448         entityIdProp = configurationProperties.find("EntityId");
449         entityInstanceProp = configurationProperties.find("EntityInstance");
450         if (entityIdProp != configurationProperties.end())
451         {
452             entityId =
453                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
454         }
455         if (entityInstanceProp != configurationProperties.end())
456         {
457             entityInstance = static_cast<uint8_t>(
458                 std::get<uint64_t>(entityInstanceProp->second));
459         }
460 
461         // stop searching Association records.
462         break;
463     } // end for Association vectors.
464 
465     if constexpr (debug)
466     {
467         std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
468                      path.c_str(), entityId, entityInstance);
469     }
470 }
471 
472 } // namespace ipmi
473