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