blob: ede14bf45e74f98b7c6eb4fe3e7cbe49af6f99b1 [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
Scron Chang2703b022021-07-06 15:47:45 +080019#ifdef FEATURE_HYBRID_SENSORS
20
21#include <ipmid/utils.hpp>
22namespace ipmi
23{
24namespace sensor
25{
26extern const IdInfoMap sensors;
27} // namespace sensor
28} // namespace ipmi
29
30#endif
31
Willy Tude54f482021-01-26 15:59:09 -080032namespace details
33{
Kuiying Wanga8b5b262021-02-06 23:38:22 +080034uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
Willy Tude54f482021-01-26 15:59:09 -080035{
36 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +080037 static uint16_t sensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -080038 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
39 static sdbusplus::bus::match::match sensorAdded(
40 *dbus,
41 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
42 "sensors/'",
43 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
44
45 static sdbusplus::bus::match::match sensorRemoved(
46 *dbus,
47 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
48 "openbmc_project/sensors/'",
49 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
50
Willy Tude54f482021-01-26 15:59:09 -080051 if (sensorTreePtr)
52 {
53 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +080054 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -080055 }
56
57 sensorTreePtr = std::make_shared<SensorSubTree>();
58
Willy Tude54f482021-01-26 15:59:09 -080059 static constexpr const int32_t depth = 2;
Hao Jiang9a5b51e2021-01-06 10:32:22 -080060
61 auto lbdUpdateSensorTree = [&dbus](const char* path,
62 const auto& interfaces) {
63 auto mapperCall = dbus->new_method_call(
64 "xyz.openbmc_project.ObjectMapper",
65 "/xyz/openbmc_project/object_mapper",
66 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
67 SensorSubTree sensorTreePartial;
68
69 mapperCall.append(path, depth, interfaces);
70
71 try
72 {
73 auto mapperReply = dbus->call(mapperCall);
74 mapperReply.read(sensorTreePartial);
75 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -050076 catch (const sdbusplus::exception_t& e)
Hao Jiang9a5b51e2021-01-06 10:32:22 -080077 {
78 phosphor::logging::log<phosphor::logging::level::ERR>(
79 "fail to update subtree",
80 phosphor::logging::entry("PATH=%s", path),
81 phosphor::logging::entry("WHAT=%s", e.what()));
82 return false;
83 }
84 if constexpr (debug)
85 {
86 std::fprintf(stderr, "IPMI updated: %zu sensors under %s\n",
87 sensorTreePartial.size(), path);
88 }
89 sensorTreePtr->merge(std::move(sensorTreePartial));
90 return true;
91 };
92
93 // Add sensors to SensorTree
94 static constexpr const std::array sensorInterfaces = {
Willy Tude54f482021-01-26 15:59:09 -080095 "xyz.openbmc_project.Sensor.Value",
96 "xyz.openbmc_project.Sensor.Threshold.Warning",
97 "xyz.openbmc_project.Sensor.Threshold.Critical"};
Hao Jiang9a5b51e2021-01-06 10:32:22 -080098 static constexpr const std::array vrInterfaces = {
99 "xyz.openbmc_project.Control.VoltageRegulatorMode"};
Willy Tude54f482021-01-26 15:59:09 -0800100
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800101 bool sensorRez =
102 lbdUpdateSensorTree("/xyz/openbmc_project/sensors", sensorInterfaces);
103
Scron Chang2703b022021-07-06 15:47:45 +0800104#ifdef FEATURE_HYBRID_SENSORS
105
106 if (!ipmi::sensor::sensors.empty())
107 {
108 for (const auto& sensor : ipmi::sensor::sensors)
109 {
110 // Threshold sensors should not be emplaced in here.
111 if (boost::starts_with(sensor.second.sensorPath,
112 "/xyz/openbmc_project/sensors/"))
113 {
114 continue;
115 }
116
117 // The bus service name is not listed in ipmi::sensor::Info. Give it
118 // an empty string. For those function using non-threshold sensors,
119 // the bus service name will be retrieved in an alternative way.
120 boost::container::flat_map<std::string, std::vector<std::string>>
121 connectionMap{
122 {"", {sensor.second.propertyInterfaces.begin()->first}}};
123 sensorTreePtr->emplace(sensor.second.sensorPath, connectionMap);
124 }
125 }
126
127#endif
128
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800129 // Error if searching for sensors failed.
130 if (!sensorRez)
Willy Tude54f482021-01-26 15:59:09 -0800131 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800132 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800133 }
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800134
135 // Add VR control as optional search path.
136 (void)lbdUpdateSensorTree("/xyz/openbmc_project/vr", vrInterfaces);
137
Willy Tude54f482021-01-26 15:59:09 -0800138 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800139 sensorUpdatedIndex++;
Josh Lehana55c9532020-10-28 21:59:06 -0700140 // The SDR is being regenerated, wipe the old stats
141 sdrStatsTable.wipeTable();
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800142 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800143}
144
145bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
146{
147 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
148 bool sensorNumMapUpated = false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800149 static uint16_t prevSensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -0800150 std::shared_ptr<SensorSubTree> sensorTree;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800151 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
Willy Tude54f482021-01-26 15:59:09 -0800152 if (!sensorTree)
153 {
154 return sensorNumMapUpated;
155 }
156
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800157 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
Willy Tude54f482021-01-26 15:59:09 -0800158 {
159 sensorNumMap = sensorNumMapPtr;
160 return sensorNumMapUpated;
161 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800162 prevSensorUpdatedIndex = curSensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800163
164 sensorNumMapPtr = std::make_shared<SensorNumMap>();
165
166 uint16_t sensorNum = 0;
167 uint16_t sensorIndex = 0;
168 for (const auto& sensor : *sensorTree)
169 {
170 sensorNumMapPtr->insert(
171 SensorNumMap::value_type(sensorNum, sensor.first));
172 sensorIndex++;
173 if (sensorIndex == maxSensorsPerLUN)
174 {
175 sensorIndex = lun1Sensor0;
176 }
177 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
178 {
179 // Skip assigning LUN 0x2 any sensors
180 sensorIndex = lun3Sensor0;
181 }
182 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
183 {
184 // this is an error, too many IPMI sensors
185 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
186 }
187 sensorNum = sensorIndex;
188 }
189 sensorNumMap = sensorNumMapPtr;
190 sensorNumMapUpated = true;
191 return sensorNumMapUpated;
192}
193} // namespace details
194
195bool getSensorSubtree(SensorSubTree& subtree)
196{
197 std::shared_ptr<SensorSubTree> sensorTree;
198 details::getSensorSubtree(sensorTree);
199 if (!sensorTree)
200 {
201 return false;
202 }
203
204 subtree = *sensorTree;
205 return true;
206}
207
Scron Chang2703b022021-07-06 15:47:45 +0800208#ifdef FEATURE_HYBRID_SENSORS
209// Static sensors are listed in sensor-gen.cpp.
210ipmi::sensor::IdInfoMap::const_iterator
211 findStaticSensor(const std::string& path)
212{
213 return std::find_if(
214 ipmi::sensor::sensors.begin(), ipmi::sensor::sensors.end(),
215 [&path](const ipmi::sensor::IdInfoMap::value_type& findSensor) {
216 return findSensor.second.sensorPath == path;
217 });
218}
219#endif
220
Willy Tude54f482021-01-26 15:59:09 -0800221std::string getSensorTypeStringFromPath(const std::string& path)
222{
223 // get sensor type string from path, path is defined as
224 // /xyz/openbmc_project/sensors/<type>/label
225 size_t typeEnd = path.rfind("/");
226 if (typeEnd == std::string::npos)
227 {
228 return path;
229 }
230 size_t typeStart = path.rfind("/", typeEnd - 1);
231 if (typeStart == std::string::npos)
232 {
233 return path;
234 }
235 // Start at the character after the '/'
236 typeStart++;
237 return path.substr(typeStart, typeEnd - typeStart);
238}
239
240uint8_t getSensorTypeFromPath(const std::string& path)
241{
242 uint8_t sensorType = 0;
243 std::string type = getSensorTypeStringFromPath(path);
244 auto findSensor = sensorTypes.find(type.c_str());
245 if (findSensor != sensorTypes.end())
246 {
Scron Chang2b42d7e2021-07-06 15:45:47 +0800247 sensorType =
248 static_cast<uint8_t>(std::get<sensorTypeCodes>(findSensor->second));
Willy Tude54f482021-01-26 15:59:09 -0800249 } // else default 0x0 RESERVED
250
251 return sensorType;
252}
253
254uint16_t getSensorNumberFromPath(const std::string& path)
255{
256 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
257 details::getSensorNumMap(sensorNumMapPtr);
258 if (!sensorNumMapPtr)
259 {
260 return invalidSensorNumber;
261 }
262
263 try
264 {
265 return sensorNumMapPtr->right.at(path);
266 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500267 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800268 {
269 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
270 return invalidSensorNumber;
271 }
272}
273
274uint8_t getSensorEventTypeFromPath(const std::string& path)
275{
Scron Chang2b42d7e2021-07-06 15:45:47 +0800276 uint8_t sensorEventType = 0;
277 std::string type = getSensorTypeStringFromPath(path);
278 auto findSensor = sensorTypes.find(type.c_str());
279 if (findSensor != sensorTypes.end())
280 {
281 sensorEventType = static_cast<uint8_t>(
282 std::get<sensorEventTypeCodes>(findSensor->second));
283 }
284
285 return sensorEventType;
Willy Tude54f482021-01-26 15:59:09 -0800286}
287
288std::string getPathFromSensorNumber(uint16_t sensorNum)
289{
290 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
291 details::getSensorNumMap(sensorNumMapPtr);
292 if (!sensorNumMapPtr)
293 {
294 return std::string();
295 }
296
297 try
298 {
299 return sensorNumMapPtr->left.at(sensorNum);
300 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500301 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800302 {
303 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
304 return std::string();
305 }
306}
307
308namespace ipmi
309{
310
311std::map<std::string, std::vector<std::string>>
312 getObjectInterfaces(const char* path)
313{
314 std::map<std::string, std::vector<std::string>> interfacesResponse;
315 std::vector<std::string> interfaces;
316 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
317
318 sdbusplus::message::message getObjectMessage =
319 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
320 "/xyz/openbmc_project/object_mapper",
321 "xyz.openbmc_project.ObjectMapper", "GetObject");
322 getObjectMessage.append(path, interfaces);
323
324 try
325 {
326 sdbusplus::message::message response = dbus->call(getObjectMessage);
327 response.read(interfacesResponse);
328 }
329 catch (const std::exception& e)
330 {
331 phosphor::logging::log<phosphor::logging::level::ERR>(
332 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
333 phosphor::logging::entry("WHAT=%s", e.what()));
334 }
335
336 return interfacesResponse;
337}
338
339std::map<std::string, Value> getEntityManagerProperties(const char* path,
340 const char* interface)
341{
342 std::map<std::string, Value> properties;
343 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
344
345 sdbusplus::message::message getProperties =
346 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
347 "org.freedesktop.DBus.Properties", "GetAll");
348 getProperties.append(interface);
349
350 try
351 {
352 sdbusplus::message::message response = dbus->call(getProperties);
353 response.read(properties);
354 }
355 catch (const std::exception& e)
356 {
357 phosphor::logging::log<phosphor::logging::level::ERR>(
358 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
359 phosphor::logging::entry("INTF=%s", interface),
360 phosphor::logging::entry("WHAT=%s", e.what()));
361 }
362
363 return properties;
364}
365
366const std::string* getSensorConfigurationInterface(
367 const std::map<std::string, std::vector<std::string>>&
368 sensorInterfacesResponse)
369{
370 auto entityManagerService =
371 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
372 if (entityManagerService == sensorInterfacesResponse.end())
373 {
374 return nullptr;
375 }
376
377 // Find the fan configuration first (fans can have multiple configuration
378 // interfaces).
379 for (const auto& entry : entityManagerService->second)
380 {
381 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
382 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
383 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
384 {
385 return &entry;
386 }
387 }
388
389 for (const auto& entry : entityManagerService->second)
390 {
391 if (boost::algorithm::starts_with(entry,
392 "xyz.openbmc_project.Configuration."))
393 {
394 return &entry;
395 }
396 }
397
398 return nullptr;
399}
400
401// Follow Association properties for Sensor back to the Board dbus object to
402// check for an EntityId and EntityInstance property.
403void updateIpmiFromAssociation(const std::string& path,
404 const DbusInterfaceMap& sensorMap,
405 uint8_t& entityId, uint8_t& entityInstance)
406{
407 namespace fs = std::filesystem;
408
409 auto sensorAssociationObject =
410 sensorMap.find("xyz.openbmc_project.Association.Definitions");
411 if (sensorAssociationObject == sensorMap.end())
412 {
413 if constexpr (debug)
414 {
415 std::fprintf(stderr, "path=%s, no association interface found\n",
416 path.c_str());
417 }
418
419 return;
420 }
421
422 auto associationObject =
423 sensorAssociationObject->second.find("Associations");
424 if (associationObject == sensorAssociationObject->second.end())
425 {
426 if constexpr (debug)
427 {
428 std::fprintf(stderr, "path=%s, no association records found\n",
429 path.c_str());
430 }
431
432 return;
433 }
434
435 std::vector<Association> associationValues =
436 std::get<std::vector<Association>>(associationObject->second);
437
438 // loop through the Associations looking for the right one:
439 for (const auto& entry : associationValues)
440 {
441 // forward, reverse, endpoint
442 const std::string& forward = std::get<0>(entry);
443 const std::string& reverse = std::get<1>(entry);
444 const std::string& endpoint = std::get<2>(entry);
445
446 // We only currently concern ourselves with chassis+all_sensors.
447 if (!(forward == "chassis" && reverse == "all_sensors"))
448 {
449 continue;
450 }
451
452 // the endpoint is the board entry provided by
453 // Entity-Manager. so let's grab its properties if it has
454 // the right interface.
455
456 // just try grabbing the properties first.
457 std::map<std::string, Value> ipmiProperties =
458 getEntityManagerProperties(
459 endpoint.c_str(),
460 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
461
462 auto entityIdProp = ipmiProperties.find("EntityId");
463 auto entityInstanceProp = ipmiProperties.find("EntityInstance");
464 if (entityIdProp != ipmiProperties.end())
465 {
466 entityId =
467 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
468 }
469 if (entityInstanceProp != ipmiProperties.end())
470 {
471 entityInstance = static_cast<uint8_t>(
472 std::get<uint64_t>(entityInstanceProp->second));
473 }
474
475 // Now check the entity-manager entry for this sensor to see
476 // if it has its own value and use that instead.
477 //
478 // In theory, checking this first saves us from checking
479 // both, except in most use-cases identified, there won't be
480 // a per sensor override, so we need to always check both.
481 std::string sensorNameFromPath = fs::path(path).filename();
482
483 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
484
485 // Download the interfaces for the sensor from
486 // Entity-Manager to find the name of the configuration
487 // interface.
488 std::map<std::string, std::vector<std::string>>
489 sensorInterfacesResponse =
490 getObjectInterfaces(sensorConfigPath.c_str());
491
492 const std::string* configurationInterface =
493 getSensorConfigurationInterface(sensorInterfacesResponse);
494
495 // We didnt' find a configuration interface for this sensor, but we
496 // followed the Association property to get here, so we're done
497 // searching.
498 if (!configurationInterface)
499 {
500 break;
501 }
502
503 // We found a configuration interface.
504 std::map<std::string, Value> configurationProperties =
505 getEntityManagerProperties(sensorConfigPath.c_str(),
506 configurationInterface->c_str());
507
508 entityIdProp = configurationProperties.find("EntityId");
509 entityInstanceProp = configurationProperties.find("EntityInstance");
510 if (entityIdProp != configurationProperties.end())
511 {
512 entityId =
513 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
514 }
515 if (entityInstanceProp != configurationProperties.end())
516 {
517 entityInstance = static_cast<uint8_t>(
518 std::get<uint64_t>(entityInstanceProp->second));
519 }
520
521 // stop searching Association records.
522 break;
523 } // end for Association vectors.
524
525 if constexpr (debug)
526 {
527 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
528 path.c_str(), entityId, entityInstance);
529 }
530}
531
532} // namespace ipmi