blob: b2d4a74c6c3b597f8ea964e8cd9da4e54ff13ebb [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{
Kuiying Wang17eadbf2021-02-06 23:38:22 +080060inline static uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
Jason M. Billsa9423b62018-11-29 10:25:16 -080061{
62 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Kuiying Wang17eadbf2021-02-06 23:38:22 +080063 static uint16_t sensorUpdatedIndex = 0;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070064 sd_bus* bus = NULL;
65 int ret = sd_bus_default_system(&bus);
66 if (ret < 0)
67 {
68 phosphor::logging::log<phosphor::logging::level::ERR>(
69 "Failed to connect to system bus",
70 phosphor::logging::entry("ERRNO=0x%X", -ret));
71 sd_bus_unref(bus);
Kuiying Wang17eadbf2021-02-06 23:38:22 +080072 return sensorUpdatedIndex;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -070073 }
74 sdbusplus::bus::bus dbus(bus);
Jason M. Billsa9423b62018-11-29 10:25:16 -080075 static sdbusplus::bus::match::match sensorAdded(
76 dbus,
77 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
78 "sensors/'",
79 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
80
81 static sdbusplus::bus::match::match sensorRemoved(
82 dbus,
83 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
84 "openbmc_project/sensors/'",
85 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
86
Jason M. Billsa9423b62018-11-29 10:25:16 -080087 if (sensorTreePtr)
88 {
89 subtree = sensorTreePtr;
Kuiying Wang17eadbf2021-02-06 23:38:22 +080090 return sensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -080091 }
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());
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800114 return sensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800115 }
116 subtree = sensorTreePtr;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800117 sensorUpdatedIndex++;
118 return sensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800119}
120
121inline static bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
122{
123 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
124 bool sensorNumMapUpated = false;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800125 static uint16_t prevSensorUpdatedIndex = 0;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800126 std::shared_ptr<SensorSubTree> sensorTree;
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800127 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
Jason M. Billsa9423b62018-11-29 10:25:16 -0800128 if (!sensorTree)
129 {
130 return sensorNumMapUpated;
131 }
132
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800133 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
Jason M. Billsa9423b62018-11-29 10:25:16 -0800134 {
135 sensorNumMap = sensorNumMapPtr;
136 return sensorNumMapUpated;
137 }
Kuiying Wang17eadbf2021-02-06 23:38:22 +0800138 prevSensorUpdatedIndex = curSensorUpdatedIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800139
140 sensorNumMapPtr = std::make_shared<SensorNumMap>();
141
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700142 uint16_t sensorNum = 0;
143 uint16_t sensorIndex = 0;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800144 for (const auto& sensor : *sensorTree)
145 {
146 sensorNumMapPtr->insert(
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700147 SensorNumMap::value_type(sensorNum, sensor.first));
148 sensorIndex++;
149 if (sensorIndex == maxSensorsPerLUN)
150 {
151 sensorIndex = lun1Sensor0;
152 }
153 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
154 {
155 // Skip assigning LUN 0x2 any sensors
156 sensorIndex = lun3Sensor0;
157 }
158 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
159 {
160 // this is an error, too many IPMI sensors
161 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
162 }
163 sensorNum = sensorIndex;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800164 }
165 sensorNumMap = sensorNumMapPtr;
166 sensorNumMapUpated = true;
167 return sensorNumMapUpated;
168}
169} // namespace details
170
171inline static bool getSensorSubtree(SensorSubTree& subtree)
172{
173 std::shared_ptr<SensorSubTree> sensorTree;
174 details::getSensorSubtree(sensorTree);
175 if (!sensorTree)
176 {
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700177 return false;
178 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800179
180 subtree = *sensorTree;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700181 return true;
182}
183
184struct CmpStr
185{
186 bool operator()(const char* a, const char* b) const
187 {
188 return std::strcmp(a, b) < 0;
189 }
190};
191
Jason M. Bills52341e82018-11-28 17:34:43 -0800192enum class SensorTypeCodes : uint8_t
193{
194 reserved = 0x0,
195 temperature = 0x1,
196 voltage = 0x2,
197 current = 0x3,
198 fan = 0x4,
199 other = 0xB,
200};
201
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700202const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
203 sensorTypes{{{"temperature", SensorTypeCodes::temperature},
204 {"voltage", SensorTypeCodes::voltage},
205 {"current", SensorTypeCodes::current},
206 {"fan_tach", SensorTypeCodes::fan},
James Feistf426f332019-01-04 12:48:16 -0800207 {"fan_pwm", SensorTypeCodes::fan},
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700208 {"power", SensorTypeCodes::other}}};
209
210inline static std::string getSensorTypeStringFromPath(const std::string& path)
211{
212 // get sensor type string from path, path is defined as
213 // /xyz/openbmc_project/sensors/<type>/label
214 size_t typeEnd = path.rfind("/");
Jason M. Bills360f5932018-11-14 09:08:54 -0800215 if (typeEnd == std::string::npos)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700216 {
Jason M. Bills360f5932018-11-14 09:08:54 -0800217 return path;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700218 }
Jason M. Bills360f5932018-11-14 09:08:54 -0800219 size_t typeStart = path.rfind("/", typeEnd - 1);
220 if (typeStart == std::string::npos)
221 {
222 return path;
223 }
224 // Start at the character after the '/'
225 typeStart++;
226 return path.substr(typeStart, typeEnd - typeStart);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700227}
228
229inline static uint8_t getSensorTypeFromPath(const std::string& path)
230{
231 uint8_t sensorType = 0;
232 std::string type = getSensorTypeStringFromPath(path);
233 auto findSensor = sensorTypes.find(type.c_str());
234 if (findSensor != sensorTypes.end())
235 {
236 sensorType = static_cast<uint8_t>(findSensor->second);
237 } // else default 0x0 RESERVED
238
239 return sensorType;
240}
241
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700242inline static uint16_t getSensorNumberFromPath(const std::string& path)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700243{
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 {
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700248 return invalidSensorNumber;
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700249 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800250
251 try
252 {
253 return sensorNumMapPtr->right.at(path);
254 }
255 catch (std::out_of_range& e)
256 {
257 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700258 return invalidSensorNumber;
Jason M. Billsa9423b62018-11-29 10:25:16 -0800259 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700260}
261
262inline static uint8_t getSensorEventTypeFromPath(const std::string& path)
263{
264 // TODO: Add support for additional reading types as needed
265 return 0x1; // reading type = threshold
266}
267
Johnathan Mantey308c3a82020-07-22 11:50:54 -0700268inline static std::string getPathFromSensorNumber(uint16_t sensorNum)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700269{
Jason M. Billsa9423b62018-11-29 10:25:16 -0800270 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
271 details::getSensorNumMap(sensorNumMapPtr);
272 if (!sensorNumMapPtr)
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700273 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800274 return std::string();
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700275 }
276
Jason M. Billsa9423b62018-11-29 10:25:16 -0800277 try
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700278 {
Jason M. Billsa9423b62018-11-29 10:25:16 -0800279 return sensorNumMapPtr->left.at(sensorNum);
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700280 }
Jason M. Billsa9423b62018-11-29 10:25:16 -0800281 catch (std::out_of_range& e)
282 {
283 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
284 return std::string();
285 }
Jason M. Bills3f7c5e42018-10-03 14:00:41 -0700286}
Patrick Venture262276f2019-10-18 13:27:59 -0700287
288namespace ipmi
289{
290
291static inline std::map<std::string, std::vector<std::string>>
292 getObjectInterfaces(const char* path)
293{
294 std::map<std::string, std::vector<std::string>> interfacesResponse;
295 std::vector<std::string> interfaces;
296 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
297
298 sdbusplus::message::message getObjectMessage =
299 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
300 "/xyz/openbmc_project/object_mapper",
301 "xyz.openbmc_project.ObjectMapper", "GetObject");
302 getObjectMessage.append(path, interfaces);
303
304 try
305 {
306 sdbusplus::message::message response = dbus->call(getObjectMessage);
307 response.read(interfacesResponse);
308 }
309 catch (const std::exception& e)
310 {
311 phosphor::logging::log<phosphor::logging::level::ERR>(
312 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
313 phosphor::logging::entry("WHAT=%s", e.what()));
314 }
315
316 return interfacesResponse;
317}
318
319static inline std::map<std::string, DbusVariant>
320 getEntityManagerProperties(const char* path, const char* interface)
321{
322 std::map<std::string, DbusVariant> properties;
323 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
324
325 sdbusplus::message::message getProperties =
326 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
327 "org.freedesktop.DBus.Properties", "GetAll");
328 getProperties.append(interface);
329
330 try
331 {
332 sdbusplus::message::message response = dbus->call(getProperties);
333 response.read(properties);
334 }
335 catch (const std::exception& e)
336 {
337 phosphor::logging::log<phosphor::logging::level::ERR>(
338 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
339 phosphor::logging::entry("INTF=%s", interface),
340 phosphor::logging::entry("WHAT=%s", e.what()));
341 }
342
343 return properties;
344}
345
346static inline const std::string* getSensorConfigurationInterface(
347 const std::map<std::string, std::vector<std::string>>&
348 sensorInterfacesResponse)
349{
350 auto entityManagerService =
351 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
352 if (entityManagerService == sensorInterfacesResponse.end())
353 {
354 return nullptr;
355 }
356
357 // Find the fan configuration first (fans can have multiple configuration
358 // interfaces).
359 for (const auto& entry : entityManagerService->second)
360 {
361 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
362 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
363 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
364 {
365 return &entry;
366 }
367 }
368
369 for (const auto& entry : entityManagerService->second)
370 {
371 if (boost::algorithm::starts_with(entry,
372 "xyz.openbmc_project.Configuration."))
373 {
374 return &entry;
375 }
376 }
377
378 return nullptr;
379}
380
381// Follow Association properties for Sensor back to the Board dbus object to
382// check for an EntityId and EntityInstance property.
383static inline void updateIpmiFromAssociation(const std::string& path,
384 const SensorMap& sensorMap,
385 uint8_t& entityId,
386 uint8_t& entityInstance)
387{
388 namespace fs = std::filesystem;
389
390 auto sensorAssociationObject =
391 sensorMap.find("xyz.openbmc_project.Association.Definitions");
392 if (sensorAssociationObject == sensorMap.end())
393 {
394 if constexpr (debug)
395 {
396 std::fprintf(stderr, "path=%s, no association interface found\n",
397 path.c_str());
398 }
399
400 return;
401 }
402
403 auto associationObject =
404 sensorAssociationObject->second.find("Associations");
405 if (associationObject == sensorAssociationObject->second.end())
406 {
407 if constexpr (debug)
408 {
409 std::fprintf(stderr, "path=%s, no association records found\n",
410 path.c_str());
411 }
412
413 return;
414 }
415
416 std::vector<Association> associationValues =
417 std::get<std::vector<Association>>(associationObject->second);
418
419 // loop through the Associations looking for the right one:
420 for (const auto& entry : associationValues)
421 {
422 // forward, reverse, endpoint
423 const std::string& forward = std::get<0>(entry);
424 const std::string& reverse = std::get<1>(entry);
425 const std::string& endpoint = std::get<2>(entry);
426
427 // We only currently concern ourselves with chassis+all_sensors.
428 if (!(forward == "chassis" && reverse == "all_sensors"))
429 {
430 continue;
431 }
432
433 // the endpoint is the board entry provided by
434 // Entity-Manager. so let's grab its properties if it has
435 // the right interface.
436
437 // just try grabbing the properties first.
438 std::map<std::string, DbusVariant> ipmiProperties =
439 getEntityManagerProperties(
440 endpoint.c_str(),
441 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
442
443 auto entityIdProp = ipmiProperties.find("EntityId");
444 auto entityInstanceProp = ipmiProperties.find("EntityInstance");
445 if (entityIdProp != ipmiProperties.end())
446 {
447 entityId =
448 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
449 }
450 if (entityInstanceProp != ipmiProperties.end())
451 {
452 entityInstance = static_cast<uint8_t>(
453 std::get<uint64_t>(entityInstanceProp->second));
454 }
455
456 // Now check the entity-manager entry for this sensor to see
457 // if it has its own value and use that instead.
458 //
459 // In theory, checking this first saves us from checking
460 // both, except in most use-cases identified, there won't be
461 // a per sensor override, so we need to always check both.
462 std::string sensorNameFromPath = fs::path(path).filename();
463
464 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
465
466 // Download the interfaces for the sensor from
467 // Entity-Manager to find the name of the configuration
468 // interface.
469 std::map<std::string, std::vector<std::string>>
470 sensorInterfacesResponse =
471 getObjectInterfaces(sensorConfigPath.c_str());
472
473 const std::string* configurationInterface =
474 getSensorConfigurationInterface(sensorInterfacesResponse);
475
476 // We didnt' find a configuration interface for this sensor, but we
477 // followed the Association property to get here, so we're done
478 // searching.
479 if (!configurationInterface)
480 {
481 break;
482 }
483
484 // We found a configuration interface.
485 std::map<std::string, DbusVariant> configurationProperties =
486 getEntityManagerProperties(sensorConfigPath.c_str(),
487 configurationInterface->c_str());
488
489 entityIdProp = configurationProperties.find("EntityId");
490 entityInstanceProp = configurationProperties.find("EntityInstance");
491 if (entityIdProp != configurationProperties.end())
492 {
493 entityId =
494 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
495 }
496 if (entityInstanceProp != configurationProperties.end())
497 {
498 entityInstance = static_cast<uint8_t>(
499 std::get<uint64_t>(entityInstanceProp->second));
500 }
501
502 // stop searching Association records.
503 break;
504 } // end for Association vectors.
505
506 if constexpr (debug)
507 {
508 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
509 path.c_str(), entityId, entityInstance);
510 }
511}
512
513} // namespace ipmi