blob: 055043a5c0e80499a4ff55e0c85c6f6a0bfa56d3 [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"
18
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070019#include <boost/algorithm/string.hpp>
Jason M. Billsa9423b62018-11-29 10:25:16 -080020#include <boost/bimap.hpp>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070021#include <boost/container/flat_map.hpp>
James Feistfcd2d3a2020-05-28 10:38:15 -070022#include <phosphor-logging/log.hpp>
23#include <sdbusplus/bus/match.hpp>
24
Patrick Venture262276f2019-10-18 13:27:59 -070025#include <cstdio>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070026#include <cstring>
Patrick Venture262276f2019-10-18 13:27:59 -070027#include <exception>
28#include <filesystem>
29#include <map>
Patrick Venture262276f2019-10-18 13:27:59 -070030#include <string>
31#include <vector>
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070032
33#pragma once
34
35struct CmpStrVersion
36{
37 bool operator()(std::string a, std::string b) const
38 {
39 return strverscmp(a.c_str(), b.c_str()) < 0;
40 }
41};
42
43using SensorSubTree = boost::container::flat_map<
44 std::string,
45 boost::container::flat_map<std::string, std::vector<std::string>>,
46 CmpStrVersion>;
47
Jason M. Billsa9423b62018-11-29 10:25:16 -080048using SensorNumMap = boost::bimap<int, std::string>;
49
50namespace details
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070051{
Jason M. Billsa9423b62018-11-29 10:25:16 -080052inline static bool getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
53{
54 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070055 sd_bus* bus = NULL;
56 int ret = sd_bus_default_system(&bus);
57 if (ret < 0)
58 {
59 phosphor::logging::log<phosphor::logging::level::ERR>(
60 "Failed to connect to system bus",
61 phosphor::logging::entry("ERRNO=0x%X", -ret));
62 sd_bus_unref(bus);
63 return false;
64 }
65 sdbusplus::bus::bus dbus(bus);
Jason M. Billsa9423b62018-11-29 10:25:16 -080066 static sdbusplus::bus::match::match sensorAdded(
67 dbus,
68 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
69 "sensors/'",
70 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
71
72 static sdbusplus::bus::match::match sensorRemoved(
73 dbus,
74 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
75 "openbmc_project/sensors/'",
76 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
77
78 bool sensorTreeUpdated = false;
79 if (sensorTreePtr)
80 {
81 subtree = sensorTreePtr;
82 return sensorTreeUpdated;
83 }
84
85 sensorTreePtr = std::make_shared<SensorSubTree>();
86
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070087 auto mapperCall =
88 dbus.new_method_call("xyz.openbmc_project.ObjectMapper",
89 "/xyz/openbmc_project/object_mapper",
90 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
Jason M. Bills52341e82018-11-28 17:34:43 -080091 static constexpr const auto depth = 2;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070092 static constexpr std::array<const char*, 3> interfaces = {
93 "xyz.openbmc_project.Sensor.Value",
94 "xyz.openbmc_project.Sensor.Threshold.Warning",
95 "xyz.openbmc_project.Sensor.Threshold.Critical"};
96 mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
97
98 try
99 {
100 auto mapperReply = dbus.call(mapperCall);
Jason M. Billsa9423b62018-11-29 10:25:16 -0800101 mapperReply.read(*sensorTreePtr);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700102 }
Jason M. Bills52341e82018-11-28 17:34:43 -0800103 catch (sdbusplus::exception_t& e)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700104 {
Jason M. Bills52341e82018-11-28 17:34:43 -0800105 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Jason M. Billsa9423b62018-11-29 10:25:16 -0800106 return sensorTreeUpdated;
107 }
108 subtree = sensorTreePtr;
109 sensorTreeUpdated = true;
110 return sensorTreeUpdated;
111}
112
113inline static bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
114{
115 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
116 bool sensorNumMapUpated = false;
117
118 std::shared_ptr<SensorSubTree> sensorTree;
119 bool sensorTreeUpdated = details::getSensorSubtree(sensorTree);
120 if (!sensorTree)
121 {
122 return sensorNumMapUpated;
123 }
124
125 if (!sensorTreeUpdated && sensorNumMapPtr)
126 {
127 sensorNumMap = sensorNumMapPtr;
128 return sensorNumMapUpated;
129 }
130
131 sensorNumMapPtr = std::make_shared<SensorNumMap>();
132
Jason M. Billscaed9052019-06-14 10:42:20 -0700133 uint8_t sensorNum = 0;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800134 for (const auto& sensor : *sensorTree)
135 {
136 sensorNumMapPtr->insert(
137 SensorNumMap::value_type(sensorNum++, sensor.first));
138 }
139 sensorNumMap = sensorNumMapPtr;
140 sensorNumMapUpated = true;
141 return sensorNumMapUpated;
142}
143} // namespace details
144
145inline static bool getSensorSubtree(SensorSubTree& subtree)
146{
147 std::shared_ptr<SensorSubTree> sensorTree;
148 details::getSensorSubtree(sensorTree);
149 if (!sensorTree)
150 {
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700151 return false;
152 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800153
154 subtree = *sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700155 return true;
156}
157
158struct CmpStr
159{
160 bool operator()(const char* a, const char* b) const
161 {
162 return std::strcmp(a, b) < 0;
163 }
164};
165
Jason M. Bills52341e82018-11-28 17:34:43 -0800166enum class SensorTypeCodes : uint8_t
167{
168 reserved = 0x0,
169 temperature = 0x1,
170 voltage = 0x2,
171 current = 0x3,
172 fan = 0x4,
173 other = 0xB,
174};
175
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700176const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
177 sensorTypes{{{"temperature", SensorTypeCodes::temperature},
178 {"voltage", SensorTypeCodes::voltage},
179 {"current", SensorTypeCodes::current},
180 {"fan_tach", SensorTypeCodes::fan},
James Feistf426f332019-01-04 12:48:16 -0800181 {"fan_pwm", SensorTypeCodes::fan},
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700182 {"power", SensorTypeCodes::other}}};
183
184inline static std::string getSensorTypeStringFromPath(const std::string& path)
185{
186 // get sensor type string from path, path is defined as
187 // /xyz/openbmc_project/sensors/<type>/label
188 size_t typeEnd = path.rfind("/");
Jason M. Bills360f5932018-11-14 09:08:54 -0800189 if (typeEnd == std::string::npos)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700190 {
Jason M. Bills360f5932018-11-14 09:08:54 -0800191 return path;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700192 }
Jason M. Bills360f5932018-11-14 09:08:54 -0800193 size_t typeStart = path.rfind("/", typeEnd - 1);
194 if (typeStart == std::string::npos)
195 {
196 return path;
197 }
198 // Start at the character after the '/'
199 typeStart++;
200 return path.substr(typeStart, typeEnd - typeStart);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700201}
202
203inline static uint8_t getSensorTypeFromPath(const std::string& path)
204{
205 uint8_t sensorType = 0;
206 std::string type = getSensorTypeStringFromPath(path);
207 auto findSensor = sensorTypes.find(type.c_str());
208 if (findSensor != sensorTypes.end())
209 {
210 sensorType = static_cast<uint8_t>(findSensor->second);
211 } // else default 0x0 RESERVED
212
213 return sensorType;
214}
215
216inline static uint8_t getSensorNumberFromPath(const std::string& path)
217{
Jason M. Billsa9423b62018-11-29 10:25:16 -0800218 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
219 details::getSensorNumMap(sensorNumMapPtr);
220 if (!sensorNumMapPtr)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700221 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800222 return 0xFF;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700223 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800224
225 try
226 {
227 return sensorNumMapPtr->right.at(path);
228 }
229 catch (std::out_of_range& e)
230 {
231 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
232 return 0xFF;
233 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700234}
235
236inline static uint8_t getSensorEventTypeFromPath(const std::string& path)
237{
238 // TODO: Add support for additional reading types as needed
239 return 0x1; // reading type = threshold
240}
241
242inline static std::string getPathFromSensorNumber(uint8_t sensorNum)
243{
Jason M. Billsa9423b62018-11-29 10:25:16 -0800244 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
245 details::getSensorNumMap(sensorNumMapPtr);
246 if (!sensorNumMapPtr)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700247 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800248 return std::string();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700249 }
250
Jason M. Billsa9423b62018-11-29 10:25:16 -0800251 try
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700252 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800253 return sensorNumMapPtr->left.at(sensorNum);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700254 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800255 catch (std::out_of_range& e)
256 {
257 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
258 return std::string();
259 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700260}
Patrick Venture262276f2019-10-18 13:27:59 -0700261
262namespace ipmi
263{
264
265static inline std::map<std::string, std::vector<std::string>>
266 getObjectInterfaces(const char* path)
267{
268 std::map<std::string, std::vector<std::string>> interfacesResponse;
269 std::vector<std::string> interfaces;
270 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
271
272 sdbusplus::message::message getObjectMessage =
273 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
274 "/xyz/openbmc_project/object_mapper",
275 "xyz.openbmc_project.ObjectMapper", "GetObject");
276 getObjectMessage.append(path, interfaces);
277
278 try
279 {
280 sdbusplus::message::message response = dbus->call(getObjectMessage);
281 response.read(interfacesResponse);
282 }
283 catch (const std::exception& e)
284 {
285 phosphor::logging::log<phosphor::logging::level::ERR>(
286 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
287 phosphor::logging::entry("WHAT=%s", e.what()));
288 }
289
290 return interfacesResponse;
291}
292
293static inline std::map<std::string, DbusVariant>
294 getEntityManagerProperties(const char* path, const char* interface)
295{
296 std::map<std::string, DbusVariant> properties;
297 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
298
299 sdbusplus::message::message getProperties =
300 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
301 "org.freedesktop.DBus.Properties", "GetAll");
302 getProperties.append(interface);
303
304 try
305 {
306 sdbusplus::message::message response = dbus->call(getProperties);
307 response.read(properties);
308 }
309 catch (const std::exception& e)
310 {
311 phosphor::logging::log<phosphor::logging::level::ERR>(
312 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
313 phosphor::logging::entry("INTF=%s", interface),
314 phosphor::logging::entry("WHAT=%s", e.what()));
315 }
316
317 return properties;
318}
319
320static inline const std::string* getSensorConfigurationInterface(
321 const std::map<std::string, std::vector<std::string>>&
322 sensorInterfacesResponse)
323{
324 auto entityManagerService =
325 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
326 if (entityManagerService == sensorInterfacesResponse.end())
327 {
328 return nullptr;
329 }
330
331 // Find the fan configuration first (fans can have multiple configuration
332 // interfaces).
333 for (const auto& entry : entityManagerService->second)
334 {
335 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
336 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
337 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
338 {
339 return &entry;
340 }
341 }
342
343 for (const auto& entry : entityManagerService->second)
344 {
345 if (boost::algorithm::starts_with(entry,
346 "xyz.openbmc_project.Configuration."))
347 {
348 return &entry;
349 }
350 }
351
352 return nullptr;
353}
354
355// Follow Association properties for Sensor back to the Board dbus object to
356// check for an EntityId and EntityInstance property.
357static inline void updateIpmiFromAssociation(const std::string& path,
358 const SensorMap& sensorMap,
359 uint8_t& entityId,
360 uint8_t& entityInstance)
361{
362 namespace fs = std::filesystem;
363
364 auto sensorAssociationObject =
365 sensorMap.find("xyz.openbmc_project.Association.Definitions");
366 if (sensorAssociationObject == sensorMap.end())
367 {
368 if constexpr (debug)
369 {
370 std::fprintf(stderr, "path=%s, no association interface found\n",
371 path.c_str());
372 }
373
374 return;
375 }
376
377 auto associationObject =
378 sensorAssociationObject->second.find("Associations");
379 if (associationObject == sensorAssociationObject->second.end())
380 {
381 if constexpr (debug)
382 {
383 std::fprintf(stderr, "path=%s, no association records found\n",
384 path.c_str());
385 }
386
387 return;
388 }
389
390 std::vector<Association> associationValues =
391 std::get<std::vector<Association>>(associationObject->second);
392
393 // loop through the Associations looking for the right one:
394 for (const auto& entry : associationValues)
395 {
396 // forward, reverse, endpoint
397 const std::string& forward = std::get<0>(entry);
398 const std::string& reverse = std::get<1>(entry);
399 const std::string& endpoint = std::get<2>(entry);
400
401 // We only currently concern ourselves with chassis+all_sensors.
402 if (!(forward == "chassis" && reverse == "all_sensors"))
403 {
404 continue;
405 }
406
407 // the endpoint is the board entry provided by
408 // Entity-Manager. so let's grab its properties if it has
409 // the right interface.
410
411 // just try grabbing the properties first.
412 std::map<std::string, DbusVariant> ipmiProperties =
413 getEntityManagerProperties(
414 endpoint.c_str(),
415 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
416
417 auto entityIdProp = ipmiProperties.find("EntityId");
418 auto entityInstanceProp = ipmiProperties.find("EntityInstance");
419 if (entityIdProp != ipmiProperties.end())
420 {
421 entityId =
422 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
423 }
424 if (entityInstanceProp != ipmiProperties.end())
425 {
426 entityInstance = static_cast<uint8_t>(
427 std::get<uint64_t>(entityInstanceProp->second));
428 }
429
430 // Now check the entity-manager entry for this sensor to see
431 // if it has its own value and use that instead.
432 //
433 // In theory, checking this first saves us from checking
434 // both, except in most use-cases identified, there won't be
435 // a per sensor override, so we need to always check both.
436 std::string sensorNameFromPath = fs::path(path).filename();
437
438 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
439
440 // Download the interfaces for the sensor from
441 // Entity-Manager to find the name of the configuration
442 // interface.
443 std::map<std::string, std::vector<std::string>>
444 sensorInterfacesResponse =
445 getObjectInterfaces(sensorConfigPath.c_str());
446
447 const std::string* configurationInterface =
448 getSensorConfigurationInterface(sensorInterfacesResponse);
449
450 // We didnt' find a configuration interface for this sensor, but we
451 // followed the Association property to get here, so we're done
452 // searching.
453 if (!configurationInterface)
454 {
455 break;
456 }
457
458 // We found a configuration interface.
459 std::map<std::string, DbusVariant> configurationProperties =
460 getEntityManagerProperties(sensorConfigPath.c_str(),
461 configurationInterface->c_str());
462
463 entityIdProp = configurationProperties.find("EntityId");
464 entityInstanceProp = configurationProperties.find("EntityInstance");
465 if (entityIdProp != configurationProperties.end())
466 {
467 entityId =
468 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
469 }
470 if (entityInstanceProp != configurationProperties.end())
471 {
472 entityInstance = static_cast<uint8_t>(
473 std::get<uint64_t>(entityInstanceProp->second));
474 }
475
476 // stop searching Association records.
477 break;
478 } // end for Association vectors.
479
480 if constexpr (debug)
481 {
482 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
483 path.c_str(), entityId, entityInstance);
484 }
485}
486
487} // namespace ipmi