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