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