xref: /openbmc/phosphor-host-ipmid/dbus-sdr/sdrutils.cpp (revision a55c953dd2bba16f2d43b186f920b18da4a3493f)
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     auto mapperCall =
47         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
48                               "/xyz/openbmc_project/object_mapper",
49                               "xyz.openbmc_project.ObjectMapper", "GetSubTree");
50     static constexpr const int32_t depth = 2;
51     static constexpr std::array<const char*, 3> interfaces = {
52         "xyz.openbmc_project.Sensor.Value",
53         "xyz.openbmc_project.Sensor.Threshold.Warning",
54         "xyz.openbmc_project.Sensor.Threshold.Critical"};
55     mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
56 
57     try
58     {
59         auto mapperReply = dbus->call(mapperCall);
60         mapperReply.read(*sensorTreePtr);
61     }
62     catch (sdbusplus::exception_t& e)
63     {
64         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
65         return sensorUpdatedIndex;
66     }
67     subtree = sensorTreePtr;
68     sensorUpdatedIndex++;
69     // The SDR is being regenerated, wipe the old stats
70     sdrStatsTable.wipeTable();
71     return sensorUpdatedIndex;
72 }
73 
74 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
75 {
76     static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
77     bool sensorNumMapUpated = false;
78     static uint16_t prevSensorUpdatedIndex = 0;
79     std::shared_ptr<SensorSubTree> sensorTree;
80     uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
81     if (!sensorTree)
82     {
83         return sensorNumMapUpated;
84     }
85 
86     if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
87     {
88         sensorNumMap = sensorNumMapPtr;
89         return sensorNumMapUpated;
90     }
91     prevSensorUpdatedIndex = curSensorUpdatedIndex;
92 
93     sensorNumMapPtr = std::make_shared<SensorNumMap>();
94 
95     uint16_t sensorNum = 0;
96     uint16_t sensorIndex = 0;
97     for (const auto& sensor : *sensorTree)
98     {
99         sensorNumMapPtr->insert(
100             SensorNumMap::value_type(sensorNum, sensor.first));
101         sensorIndex++;
102         if (sensorIndex == maxSensorsPerLUN)
103         {
104             sensorIndex = lun1Sensor0;
105         }
106         else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
107         {
108             // Skip assigning LUN 0x2 any sensors
109             sensorIndex = lun3Sensor0;
110         }
111         else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
112         {
113             // this is an error, too many IPMI sensors
114             throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
115         }
116         sensorNum = sensorIndex;
117     }
118     sensorNumMap = sensorNumMapPtr;
119     sensorNumMapUpated = true;
120     return sensorNumMapUpated;
121 }
122 } // namespace details
123 
124 bool getSensorSubtree(SensorSubTree& subtree)
125 {
126     std::shared_ptr<SensorSubTree> sensorTree;
127     details::getSensorSubtree(sensorTree);
128     if (!sensorTree)
129     {
130         return false;
131     }
132 
133     subtree = *sensorTree;
134     return true;
135 }
136 
137 std::string getSensorTypeStringFromPath(const std::string& path)
138 {
139     // get sensor type string from path, path is defined as
140     // /xyz/openbmc_project/sensors/<type>/label
141     size_t typeEnd = path.rfind("/");
142     if (typeEnd == std::string::npos)
143     {
144         return path;
145     }
146     size_t typeStart = path.rfind("/", typeEnd - 1);
147     if (typeStart == std::string::npos)
148     {
149         return path;
150     }
151     // Start at the character after the '/'
152     typeStart++;
153     return path.substr(typeStart, typeEnd - typeStart);
154 }
155 
156 uint8_t getSensorTypeFromPath(const std::string& path)
157 {
158     uint8_t sensorType = 0;
159     std::string type = getSensorTypeStringFromPath(path);
160     auto findSensor = sensorTypes.find(type.c_str());
161     if (findSensor != sensorTypes.end())
162     {
163         sensorType = static_cast<uint8_t>(findSensor->second);
164     } // else default 0x0 RESERVED
165 
166     return sensorType;
167 }
168 
169 uint16_t getSensorNumberFromPath(const std::string& path)
170 {
171     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
172     details::getSensorNumMap(sensorNumMapPtr);
173     if (!sensorNumMapPtr)
174     {
175         return invalidSensorNumber;
176     }
177 
178     try
179     {
180         return sensorNumMapPtr->right.at(path);
181     }
182     catch (std::out_of_range& e)
183     {
184         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
185         return invalidSensorNumber;
186     }
187 }
188 
189 uint8_t getSensorEventTypeFromPath(const std::string& path)
190 {
191     // TODO: Add support for additional reading types as needed
192     return 0x1; // reading type = threshold
193 }
194 
195 std::string getPathFromSensorNumber(uint16_t sensorNum)
196 {
197     std::shared_ptr<SensorNumMap> sensorNumMapPtr;
198     details::getSensorNumMap(sensorNumMapPtr);
199     if (!sensorNumMapPtr)
200     {
201         return std::string();
202     }
203 
204     try
205     {
206         return sensorNumMapPtr->left.at(sensorNum);
207     }
208     catch (std::out_of_range& e)
209     {
210         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
211         return std::string();
212     }
213 }
214 
215 namespace ipmi
216 {
217 
218 std::map<std::string, std::vector<std::string>>
219     getObjectInterfaces(const char* path)
220 {
221     std::map<std::string, std::vector<std::string>> interfacesResponse;
222     std::vector<std::string> interfaces;
223     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
224 
225     sdbusplus::message::message getObjectMessage =
226         dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
227                               "/xyz/openbmc_project/object_mapper",
228                               "xyz.openbmc_project.ObjectMapper", "GetObject");
229     getObjectMessage.append(path, interfaces);
230 
231     try
232     {
233         sdbusplus::message::message response = dbus->call(getObjectMessage);
234         response.read(interfacesResponse);
235     }
236     catch (const std::exception& e)
237     {
238         phosphor::logging::log<phosphor::logging::level::ERR>(
239             "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
240             phosphor::logging::entry("WHAT=%s", e.what()));
241     }
242 
243     return interfacesResponse;
244 }
245 
246 std::map<std::string, Value> getEntityManagerProperties(const char* path,
247                                                         const char* interface)
248 {
249     std::map<std::string, Value> properties;
250     std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
251 
252     sdbusplus::message::message getProperties =
253         dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
254                               "org.freedesktop.DBus.Properties", "GetAll");
255     getProperties.append(interface);
256 
257     try
258     {
259         sdbusplus::message::message response = dbus->call(getProperties);
260         response.read(properties);
261     }
262     catch (const std::exception& e)
263     {
264         phosphor::logging::log<phosphor::logging::level::ERR>(
265             "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
266             phosphor::logging::entry("INTF=%s", interface),
267             phosphor::logging::entry("WHAT=%s", e.what()));
268     }
269 
270     return properties;
271 }
272 
273 const std::string* getSensorConfigurationInterface(
274     const std::map<std::string, std::vector<std::string>>&
275         sensorInterfacesResponse)
276 {
277     auto entityManagerService =
278         sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
279     if (entityManagerService == sensorInterfacesResponse.end())
280     {
281         return nullptr;
282     }
283 
284     // Find the fan configuration first (fans can have multiple configuration
285     // interfaces).
286     for (const auto& entry : entityManagerService->second)
287     {
288         if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
289             entry == "xyz.openbmc_project.Configuration.I2CFan" ||
290             entry == "xyz.openbmc_project.Configuration.NuvotonFan")
291         {
292             return &entry;
293         }
294     }
295 
296     for (const auto& entry : entityManagerService->second)
297     {
298         if (boost::algorithm::starts_with(entry,
299                                           "xyz.openbmc_project.Configuration."))
300         {
301             return &entry;
302         }
303     }
304 
305     return nullptr;
306 }
307 
308 // Follow Association properties for Sensor back to the Board dbus object to
309 // check for an EntityId and EntityInstance property.
310 void updateIpmiFromAssociation(const std::string& path,
311                                const DbusInterfaceMap& sensorMap,
312                                uint8_t& entityId, uint8_t& entityInstance)
313 {
314     namespace fs = std::filesystem;
315 
316     auto sensorAssociationObject =
317         sensorMap.find("xyz.openbmc_project.Association.Definitions");
318     if (sensorAssociationObject == sensorMap.end())
319     {
320         if constexpr (debug)
321         {
322             std::fprintf(stderr, "path=%s, no association interface found\n",
323                          path.c_str());
324         }
325 
326         return;
327     }
328 
329     auto associationObject =
330         sensorAssociationObject->second.find("Associations");
331     if (associationObject == sensorAssociationObject->second.end())
332     {
333         if constexpr (debug)
334         {
335             std::fprintf(stderr, "path=%s, no association records found\n",
336                          path.c_str());
337         }
338 
339         return;
340     }
341 
342     std::vector<Association> associationValues =
343         std::get<std::vector<Association>>(associationObject->second);
344 
345     // loop through the Associations looking for the right one:
346     for (const auto& entry : associationValues)
347     {
348         // forward, reverse, endpoint
349         const std::string& forward = std::get<0>(entry);
350         const std::string& reverse = std::get<1>(entry);
351         const std::string& endpoint = std::get<2>(entry);
352 
353         // We only currently concern ourselves with chassis+all_sensors.
354         if (!(forward == "chassis" && reverse == "all_sensors"))
355         {
356             continue;
357         }
358 
359         // the endpoint is the board entry provided by
360         // Entity-Manager. so let's grab its properties if it has
361         // the right interface.
362 
363         // just try grabbing the properties first.
364         std::map<std::string, Value> ipmiProperties =
365             getEntityManagerProperties(
366                 endpoint.c_str(),
367                 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
368 
369         auto entityIdProp = ipmiProperties.find("EntityId");
370         auto entityInstanceProp = ipmiProperties.find("EntityInstance");
371         if (entityIdProp != ipmiProperties.end())
372         {
373             entityId =
374                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
375         }
376         if (entityInstanceProp != ipmiProperties.end())
377         {
378             entityInstance = static_cast<uint8_t>(
379                 std::get<uint64_t>(entityInstanceProp->second));
380         }
381 
382         // Now check the entity-manager entry for this sensor to see
383         // if it has its own value and use that instead.
384         //
385         // In theory, checking this first saves us from checking
386         // both, except in most use-cases identified, there won't be
387         // a per sensor override, so we need to always check both.
388         std::string sensorNameFromPath = fs::path(path).filename();
389 
390         std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
391 
392         // Download the interfaces for the sensor from
393         // Entity-Manager to find the name of the configuration
394         // interface.
395         std::map<std::string, std::vector<std::string>>
396             sensorInterfacesResponse =
397                 getObjectInterfaces(sensorConfigPath.c_str());
398 
399         const std::string* configurationInterface =
400             getSensorConfigurationInterface(sensorInterfacesResponse);
401 
402         // We didnt' find a configuration interface for this sensor, but we
403         // followed the Association property to get here, so we're done
404         // searching.
405         if (!configurationInterface)
406         {
407             break;
408         }
409 
410         // We found a configuration interface.
411         std::map<std::string, Value> configurationProperties =
412             getEntityManagerProperties(sensorConfigPath.c_str(),
413                                        configurationInterface->c_str());
414 
415         entityIdProp = configurationProperties.find("EntityId");
416         entityInstanceProp = configurationProperties.find("EntityInstance");
417         if (entityIdProp != configurationProperties.end())
418         {
419             entityId =
420                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
421         }
422         if (entityInstanceProp != configurationProperties.end())
423         {
424             entityInstance = static_cast<uint8_t>(
425                 std::get<uint64_t>(entityInstanceProp->second));
426         }
427 
428         // stop searching Association records.
429         break;
430     } // end for Association vectors.
431 
432     if constexpr (debug)
433     {
434         std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
435                      path.c_str(), entityId, entityInstance);
436     }
437 }
438 
439 } // namespace ipmi
440