blob: 43a53fdc38873b72a141dad7c2d9a23c7be98b6f [file] [log] [blame]
Jason M. Bills3f7c5e42018-10-03 14:00:41 -07001/*
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
Patrick Venture262276f2019-10-18 13:27:59 -070017#include "commandutils.hpp"
Patrick Venturec2a07d42020-05-30 16:35:03 -070018#include "types.hpp"
Patrick Venture262276f2019-10-18 13:27:59 -070019
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070020#include <boost/algorithm/string.hpp>
Jason M. Billsa9423b62018-11-29 10:25:16 -080021#include <boost/bimap.hpp>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070022#include <boost/container/flat_map.hpp>
James Feistfcd2d3a2020-05-28 10:38:15 -070023#include <phosphor-logging/log.hpp>
24#include <sdbusplus/bus/match.hpp>
25
Patrick Venture262276f2019-10-18 13:27:59 -070026#include <cstdio>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070027#include <cstring>
Patrick Venture262276f2019-10-18 13:27:59 -070028#include <exception>
29#include <filesystem>
30#include <map>
Patrick Venture262276f2019-10-18 13:27:59 -070031#include <string>
32#include <vector>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070033
34#pragma once
35
36struct CmpStrVersion
37{
38 bool operator()(std::string a, std::string b) const
39 {
40 return strverscmp(a.c_str(), b.c_str()) < 0;
41 }
42};
43
44using SensorSubTree = boost::container::flat_map<
45 std::string,
46 boost::container::flat_map<std::string, std::vector<std::string>>,
47 CmpStrVersion>;
48
Jason M. Billsa9423b62018-11-29 10:25:16 -080049using SensorNumMap = boost::bimap<int, std::string>;
50
Johnathan Mantey308c3a82020-07-22 11:50:54 -070051static constexpr uint16_t maxSensorsPerLUN = 255;
52static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3);
53static constexpr uint16_t lun1Sensor0 = 0x100;
54static constexpr uint16_t lun3Sensor0 = 0x300;
55static constexpr uint16_t invalidSensorNumber = 0xFFFF;
56static constexpr uint8_t reservedSensorNumber = 0xFF;
57
Jason M. Billsa9423b62018-11-29 10:25:16 -080058namespace details
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070059{
Jason M. Billsa9423b62018-11-29 10:25:16 -080060inline static bool getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
61{
62 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070063 sd_bus* bus = NULL;
64 int ret = sd_bus_default_system(&bus);
65 if (ret < 0)
66 {
67 phosphor::logging::log<phosphor::logging::level::ERR>(
68 "Failed to connect to system bus",
69 phosphor::logging::entry("ERRNO=0x%X", -ret));
70 sd_bus_unref(bus);
71 return false;
72 }
73 sdbusplus::bus::bus dbus(bus);
Jason M. Billsa9423b62018-11-29 10:25:16 -080074 static sdbusplus::bus::match::match sensorAdded(
75 dbus,
76 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
77 "sensors/'",
78 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
79
80 static sdbusplus::bus::match::match sensorRemoved(
81 dbus,
82 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
83 "openbmc_project/sensors/'",
84 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
85
86 bool sensorTreeUpdated = false;
87 if (sensorTreePtr)
88 {
89 subtree = sensorTreePtr;
90 return sensorTreeUpdated;
91 }
92
93 sensorTreePtr = std::make_shared<SensorSubTree>();
94
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070095 auto mapperCall =
96 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
97 "/xyz/openbmc_project/object_mapper",
98 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
Jason M. Bills52341e82018-11-28 17:34:43 -080099 static constexpr const auto depth = 2;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700100 static constexpr std::array<const char*, 3> interfaces = {
101 "xyz.openbmc_project.Sensor.Value",
102 "xyz.openbmc_project.Sensor.Threshold.Warning",
103 "xyz.openbmc_project.Sensor.Threshold.Critical"};
104 mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
105
106 try
107 {
108 auto mapperReply = dbus.call(mapperCall);
Jason M. Billsa9423b62018-11-29 10:25:16 -0800109 mapperReply.read(*sensorTreePtr);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700110 }
Jason M. Bills52341e82018-11-28 17:34:43 -0800111 catch (sdbusplus::exception_t& e)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700112 {
Jason M. Bills52341e82018-11-28 17:34:43 -0800113 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jason M. Billsa9423b62018-11-29 10:25:16 -0800114 return sensorTreeUpdated;
115 }
116 subtree = sensorTreePtr;
117 sensorTreeUpdated = true;
118 return sensorTreeUpdated;
119}
120
121inline static bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
122{
123 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
124 bool sensorNumMapUpated = false;
125
126 std::shared_ptr<SensorSubTree> sensorTree;
127 bool sensorTreeUpdated = details::getSensorSubtree(sensorTree);
128 if (!sensorTree)
129 {
130 return sensorNumMapUpated;
131 }
132
133 if (!sensorTreeUpdated && sensorNumMapPtr)
134 {
135 sensorNumMap = sensorNumMapPtr;
136 return sensorNumMapUpated;
137 }
138
139 sensorNumMapPtr = std::make_shared<SensorNumMap>();
140
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700141 uint16_t sensorNum = 0;
142 uint16_t sensorIndex = 0;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800143 for (const auto& sensor : *sensorTree)
144 {
145 sensorNumMapPtr->insert(
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700146 SensorNumMap::value_type(sensorNum, sensor.first));
147 sensorIndex++;
148 if (sensorIndex == maxSensorsPerLUN)
149 {
150 sensorIndex = lun1Sensor0;
151 }
152 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
153 {
154 // Skip assigning LUN 0x2 any sensors
155 sensorIndex = lun3Sensor0;
156 }
157 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
158 {
159 // this is an error, too many IPMI sensors
160 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
161 }
162 sensorNum = sensorIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800163 }
164 sensorNumMap = sensorNumMapPtr;
165 sensorNumMapUpated = true;
166 return sensorNumMapUpated;
167}
168} // namespace details
169
170inline static bool getSensorSubtree(SensorSubTree& subtree)
171{
172 std::shared_ptr<SensorSubTree> sensorTree;
173 details::getSensorSubtree(sensorTree);
174 if (!sensorTree)
175 {
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700176 return false;
177 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800178
179 subtree = *sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700180 return true;
181}
182
183struct CmpStr
184{
185 bool operator()(const char* a, const char* b) const
186 {
187 return std::strcmp(a, b) < 0;
188 }
189};
190
Jason M. Bills52341e82018-11-28 17:34:43 -0800191enum class SensorTypeCodes : uint8_t
192{
193 reserved = 0x0,
194 temperature = 0x1,
195 voltage = 0x2,
196 current = 0x3,
197 fan = 0x4,
198 other = 0xB,
199};
200
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700201const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
202 sensorTypes{{{"temperature", SensorTypeCodes::temperature},
203 {"voltage", SensorTypeCodes::voltage},
204 {"current", SensorTypeCodes::current},
205 {"fan_tach", SensorTypeCodes::fan},
James Feistf426f332019-01-04 12:48:16 -0800206 {"fan_pwm", SensorTypeCodes::fan},
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700207 {"power", SensorTypeCodes::other}}};
208
209inline static std::string getSensorTypeStringFromPath(const std::string& path)
210{
211 // get sensor type string from path, path is defined as
212 // /xyz/openbmc_project/sensors/<type>/label
213 size_t typeEnd = path.rfind("/");
Jason M. Bills360f5932018-11-14 09:08:54 -0800214 if (typeEnd == std::string::npos)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700215 {
Jason M. Bills360f5932018-11-14 09:08:54 -0800216 return path;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700217 }
Jason M. Bills360f5932018-11-14 09:08:54 -0800218 size_t typeStart = path.rfind("/", typeEnd - 1);
219 if (typeStart == std::string::npos)
220 {
221 return path;
222 }
223 // Start at the character after the '/'
224 typeStart++;
225 return path.substr(typeStart, typeEnd - typeStart);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700226}
227
228inline static uint8_t getSensorTypeFromPath(const std::string& path)
229{
230 uint8_t sensorType = 0;
231 std::string type = getSensorTypeStringFromPath(path);
232 auto findSensor = sensorTypes.find(type.c_str());
233 if (findSensor != sensorTypes.end())
234 {
235 sensorType = static_cast<uint8_t>(findSensor->second);
236 } // else default 0x0 RESERVED
237
238 return sensorType;
239}
240
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700241inline static uint16_t getSensorNumberFromPath(const std::string& path)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700242{
Jason M. Billsa9423b62018-11-29 10:25:16 -0800243 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
244 details::getSensorNumMap(sensorNumMapPtr);
245 if (!sensorNumMapPtr)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700246 {
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700247 return invalidSensorNumber;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700248 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800249
250 try
251 {
252 return sensorNumMapPtr->right.at(path);
253 }
254 catch (std::out_of_range& e)
255 {
256 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700257 return invalidSensorNumber;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800258 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700259}
260
261inline static uint8_t getSensorEventTypeFromPath(const std::string& path)
262{
263 // TODO: Add support for additional reading types as needed
264 return 0x1; // reading type = threshold
265}
266
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700267inline static std::string getPathFromSensorNumber(uint16_t sensorNum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700268{
Jason M. Billsa9423b62018-11-29 10:25:16 -0800269 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
270 details::getSensorNumMap(sensorNumMapPtr);
271 if (!sensorNumMapPtr)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700272 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800273 return std::string();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700274 }
275
Jason M. Billsa9423b62018-11-29 10:25:16 -0800276 try
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700277 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800278 return sensorNumMapPtr->left.at(sensorNum);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700279 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800280 catch (std::out_of_range& e)
281 {
282 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
283 return std::string();
284 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700285}
Patrick Venture262276f2019-10-18 13:27:59 -0700286
287namespace ipmi
288{
289
290static inline std::map<std::string, std::vector<std::string>>
291 getObjectInterfaces(const char* path)
292{
293 std::map<std::string, std::vector<std::string>> interfacesResponse;
294 std::vector<std::string> interfaces;
295 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
296
297 sdbusplus::message::message getObjectMessage =
298 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
299 "/xyz/openbmc_project/object_mapper",
300 "xyz.openbmc_project.ObjectMapper", "GetObject");
301 getObjectMessage.append(path, interfaces);
302
303 try
304 {
305 sdbusplus::message::message response = dbus->call(getObjectMessage);
306 response.read(interfacesResponse);
307 }
308 catch (const std::exception& e)
309 {
310 phosphor::logging::log<phosphor::logging::level::ERR>(
311 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
312 phosphor::logging::entry("WHAT=%s", e.what()));
313 }
314
315 return interfacesResponse;
316}
317
318static inline std::map<std::string, DbusVariant>
319 getEntityManagerProperties(const char* path, const char* interface)
320{
321 std::map<std::string, DbusVariant> properties;
322 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
323
324 sdbusplus::message::message getProperties =
325 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
326 "org.freedesktop.DBus.Properties", "GetAll");
327 getProperties.append(interface);
328
329 try
330 {
331 sdbusplus::message::message response = dbus->call(getProperties);
332 response.read(properties);
333 }
334 catch (const std::exception& e)
335 {
336 phosphor::logging::log<phosphor::logging::level::ERR>(
337 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
338 phosphor::logging::entry("INTF=%s", interface),
339 phosphor::logging::entry("WHAT=%s", e.what()));
340 }
341
342 return properties;
343}
344
345static inline const std::string* getSensorConfigurationInterface(
346 const std::map<std::string, std::vector<std::string>>&
347 sensorInterfacesResponse)
348{
349 auto entityManagerService =
350 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
351 if (entityManagerService == sensorInterfacesResponse.end())
352 {
353 return nullptr;
354 }
355
356 // Find the fan configuration first (fans can have multiple configuration
357 // interfaces).
358 for (const auto& entry : entityManagerService->second)
359 {
360 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
361 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
362 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
363 {
364 return &entry;
365 }
366 }
367
368 for (const auto& entry : entityManagerService->second)
369 {
370 if (boost::algorithm::starts_with(entry,
371 "xyz.openbmc_project.Configuration."))
372 {
373 return &entry;
374 }
375 }
376
377 return nullptr;
378}
379
380// Follow Association properties for Sensor back to the Board dbus object to
381// check for an EntityId and EntityInstance property.
382static inline void updateIpmiFromAssociation(const std::string& path,
383 const SensorMap& sensorMap,
384 uint8_t& entityId,
385 uint8_t& entityInstance)
386{
387 namespace fs = std::filesystem;
388
389 auto sensorAssociationObject =
390 sensorMap.find("xyz.openbmc_project.Association.Definitions");
391 if (sensorAssociationObject == sensorMap.end())
392 {
393 if constexpr (debug)
394 {
395 std::fprintf(stderr, "path=%s, no association interface found\n",
396 path.c_str());
397 }
398
399 return;
400 }
401
402 auto associationObject =
403 sensorAssociationObject->second.find("Associations");
404 if (associationObject == sensorAssociationObject->second.end())
405 {
406 if constexpr (debug)
407 {
408 std::fprintf(stderr, "path=%s, no association records found\n",
409 path.c_str());
410 }
411
412 return;
413 }
414
415 std::vector<Association> associationValues =
416 std::get<std::vector<Association>>(associationObject->second);
417
418 // loop through the Associations looking for the right one:
419 for (const auto& entry : associationValues)
420 {
421 // forward, reverse, endpoint
422 const std::string& forward = std::get<0>(entry);
423 const std::string& reverse = std::get<1>(entry);
424 const std::string& endpoint = std::get<2>(entry);
425
426 // We only currently concern ourselves with chassis+all_sensors.
427 if (!(forward == "chassis" && reverse == "all_sensors"))
428 {
429 continue;
430 }
431
432 // the endpoint is the board entry provided by
433 // Entity-Manager. so let's grab its properties if it has
434 // the right interface.
435
436 // just try grabbing the properties first.
437 std::map<std::string, DbusVariant> ipmiProperties =
438 getEntityManagerProperties(
439 endpoint.c_str(),
440 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
441
442 auto entityIdProp = ipmiProperties.find("EntityId");
443 auto entityInstanceProp = ipmiProperties.find("EntityInstance");
444 if (entityIdProp != ipmiProperties.end())
445 {
446 entityId =
447 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
448 }
449 if (entityInstanceProp != ipmiProperties.end())
450 {
451 entityInstance = static_cast<uint8_t>(
452 std::get<uint64_t>(entityInstanceProp->second));
453 }
454
455 // Now check the entity-manager entry for this sensor to see
456 // if it has its own value and use that instead.
457 //
458 // In theory, checking this first saves us from checking
459 // both, except in most use-cases identified, there won't be
460 // a per sensor override, so we need to always check both.
461 std::string sensorNameFromPath = fs::path(path).filename();
462
463 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
464
465 // Download the interfaces for the sensor from
466 // Entity-Manager to find the name of the configuration
467 // interface.
468 std::map<std::string, std::vector<std::string>>
469 sensorInterfacesResponse =
470 getObjectInterfaces(sensorConfigPath.c_str());
471
472 const std::string* configurationInterface =
473 getSensorConfigurationInterface(sensorInterfacesResponse);
474
475 // We didnt' find a configuration interface for this sensor, but we
476 // followed the Association property to get here, so we're done
477 // searching.
478 if (!configurationInterface)
479 {
480 break;
481 }
482
483 // We found a configuration interface.
484 std::map<std::string, DbusVariant> configurationProperties =
485 getEntityManagerProperties(sensorConfigPath.c_str(),
486 configurationInterface->c_str());
487
488 entityIdProp = configurationProperties.find("EntityId");
489 entityInstanceProp = configurationProperties.find("EntityInstance");
490 if (entityIdProp != configurationProperties.end())
491 {
492 entityId =
493 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
494 }
495 if (entityInstanceProp != configurationProperties.end())
496 {
497 entityInstance = static_cast<uint8_t>(
498 std::get<uint64_t>(entityInstanceProp->second));
499 }
500
501 // stop searching Association records.
502 break;
503 } // end for Association vectors.
504
505 if constexpr (debug)
506 {
507 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
508 path.c_str(), entityId, entityInstance);
509 }
510}
511
512} // namespace ipmi