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