blob: e7f6566b783dc8dfd113b35cc3f48b6c0d64175e [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
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
17#include "dbus-sdr/sdrutils.hpp"
18
George Liuc1c7eac2024-02-04 17:24:19 +080019#include <ipmid/utils.hpp>
Johnathan Mantey04b0b072023-10-10 12:30:35 -070020#include <nlohmann/json.hpp>
George Liude6694e2024-07-17 15:22:25 +080021#include <phosphor-logging/lg2.hpp>
Alexander Hansen93aa9832025-11-07 15:29:49 +010022#include <xyz/openbmc_project/ObjectMapper/common.hpp>
Alexander Hansenf365c7f2025-11-14 12:08:34 +010023#include <xyz/openbmc_project/Sensor/Value/common.hpp>
Johnathan Mantey04b0b072023-10-10 12:30:35 -070024
25#include <fstream>
Willy Tu4eca2512022-06-20 21:14:51 -070026#include <optional>
27#include <unordered_set>
28
Alexander Hansen93aa9832025-11-07 15:29:49 +010029using ObjectMapper = sdbusplus::common::xyz::openbmc_project::ObjectMapper;
Alexander Hansenf365c7f2025-11-14 12:08:34 +010030using SensorValue = sdbusplus::common::xyz::openbmc_project::sensor::Value;
Alexander Hansen93aa9832025-11-07 15:29:49 +010031
Scron Chang2703b022021-07-06 15:47:45 +080032#ifdef FEATURE_HYBRID_SENSORS
33
34#include <ipmid/utils.hpp>
35namespace ipmi
36{
37namespace sensor
38{
39extern const IdInfoMap sensors;
40} // namespace sensor
41} // namespace ipmi
42
43#endif
44
Johnathan Mantey34d19732024-07-17 07:59:54 -070045boost::container::flat_map<
46 const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr>
47 sensorTypes{
48 {{"temperature", std::make_pair(SensorTypeCodes::temperature,
49 SensorEventTypeCodes::threshold)},
50 {"voltage", std::make_pair(SensorTypeCodes::voltage,
51 SensorEventTypeCodes::threshold)},
52 {"current", std::make_pair(SensorTypeCodes::current,
53 SensorEventTypeCodes::threshold)},
54 {"fan_tach", std::make_pair(SensorTypeCodes::fan,
55 SensorEventTypeCodes::threshold)},
56 {"fan_pwm", std::make_pair(SensorTypeCodes::fan,
57 SensorEventTypeCodes::threshold)},
George Liuf2807ed2025-04-03 15:52:57 +080058 {"intrusion", std::make_pair(SensorTypeCodes::physicalSecurity,
Johnathan Mantey34d19732024-07-17 07:59:54 -070059 SensorEventTypeCodes::sensorSpecified)},
60 {"processor", std::make_pair(SensorTypeCodes::processor,
61 SensorEventTypeCodes::sensorSpecified)},
62 {"power", std::make_pair(SensorTypeCodes::other,
63 SensorEventTypeCodes::threshold)},
64 {"memory", std::make_pair(SensorTypeCodes::memory,
65 SensorEventTypeCodes::sensorSpecified)},
George Liuf2807ed2025-04-03 15:52:57 +080066 {"state", std::make_pair(SensorTypeCodes::powerUnit,
Johnathan Mantey34d19732024-07-17 07:59:54 -070067 SensorEventTypeCodes::sensorSpecified)},
68 {"buttons", std::make_pair(SensorTypeCodes::buttons,
69 SensorEventTypeCodes::sensorSpecified)},
70 {"watchdog", std::make_pair(SensorTypeCodes::watchdog2,
71 SensorEventTypeCodes::sensorSpecified)},
72 {"entity", std::make_pair(SensorTypeCodes::entity,
73 SensorEventTypeCodes::sensorSpecified)},
74 {"energy", std::make_pair(SensorTypeCodes::other,
75 SensorEventTypeCodes::threshold)}}};
76
Willy Tude54f482021-01-26 15:59:09 -080077namespace details
78{
Johnathan Mantey04b0b072023-10-10 12:30:35 -070079
80// IPMI supports a smaller number of sensors than are available via Redfish.
81// Trim the list of sensors, via a configuration file.
82// Read the IPMI Sensor Filtering section in docs/configuration.md for
83// a more detailed description.
84static void filterSensors(SensorSubTree& subtree)
85{
86 constexpr const char* filterFilename =
87 "/usr/share/ipmi-providers/sensor_filter.json";
88 std::ifstream filterFile(filterFilename);
89 if (!filterFile.good())
90 {
91 return;
92 }
Patrick Williams1318a5e2024-08-16 15:19:54 -040093 nlohmann::json sensorFilterJSON =
94 nlohmann::json::parse(filterFile, nullptr, false);
Johnathan Mantey04b0b072023-10-10 12:30:35 -070095 nlohmann::json::iterator svcFilterit =
96 sensorFilterJSON.find("ServiceFilter");
97 if (svcFilterit == sensorFilterJSON.end())
98 {
99 return;
100 }
101
102 subtree.erase(std::remove_if(subtree.begin(), subtree.end(),
103 [svcFilterit](SensorSubTree::value_type& kv) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400104 auto& [_, serviceToIfaces] = kv;
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700105
Patrick Williams1318a5e2024-08-16 15:19:54 -0400106 for (auto service = svcFilterit->begin();
107 service != svcFilterit->end();
108 ++service)
109 {
110 serviceToIfaces.erase(*service);
111 }
112 return serviceToIfaces.empty();
113 }),
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700114 subtree.end());
115}
116
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800117uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
Willy Tude54f482021-01-26 15:59:09 -0800118{
119 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800120 static uint16_t sensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -0800121 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williams5d82f472022-07-22 19:26:53 -0500122 static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800123 *dbus,
124 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
125 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500126 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Willy Tude54f482021-01-26 15:59:09 -0800127
Patrick Williams5d82f472022-07-22 19:26:53 -0500128 static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800129 *dbus,
130 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
131 "openbmc_project/sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500132 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Willy Tude54f482021-01-26 15:59:09 -0800133
Willy Tude54f482021-01-26 15:59:09 -0800134 if (sensorTreePtr)
135 {
136 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800137 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800138 }
139
140 sensorTreePtr = std::make_shared<SensorSubTree>();
141
Willy Tude54f482021-01-26 15:59:09 -0800142 static constexpr const int32_t depth = 2;
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800143
Patrick Williams369824e2023-10-20 11:18:23 -0500144 auto lbdUpdateSensorTree = [&dbus](const char* path,
145 const auto& interfaces) {
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800146 auto mapperCall = dbus->new_method_call(
Alexander Hansen93aa9832025-11-07 15:29:49 +0100147 ObjectMapper::default_service, ObjectMapper::instance_path,
148 ObjectMapper::interface, ObjectMapper::method_names::get_sub_tree);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800149 SensorSubTree sensorTreePartial;
150
151 mapperCall.append(path, depth, interfaces);
152
153 try
154 {
155 auto mapperReply = dbus->call(mapperCall);
156 mapperReply.read(sensorTreePartial);
157 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500158 catch (const sdbusplus::exception_t& e)
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800159 {
George Liude6694e2024-07-17 15:22:25 +0800160 lg2::error("Failed to update subtree, path: {PATH}, error: {ERROR}",
161 "PATH", path, "ERROR", e);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800162 return false;
163 }
164 if constexpr (debug)
165 {
166 std::fprintf(stderr, "IPMI updated: %zu sensors under %s\n",
167 sensorTreePartial.size(), path);
168 }
169 sensorTreePtr->merge(std::move(sensorTreePartial));
170 return true;
171 };
172
173 // Add sensors to SensorTree
174 static constexpr const std::array sensorInterfaces = {
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100175 SensorValue::interface, "xyz.openbmc_project.Sensor.ValueMutability",
Willy Tude54f482021-01-26 15:59:09 -0800176 "xyz.openbmc_project.Sensor.Threshold.Warning",
177 "xyz.openbmc_project.Sensor.Threshold.Critical"};
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800178 static constexpr const std::array vrInterfaces = {
179 "xyz.openbmc_project.Control.VoltageRegulatorMode"};
Willy Tude54f482021-01-26 15:59:09 -0800180
Patrick Williams1318a5e2024-08-16 15:19:54 -0400181 bool sensorRez =
182 lbdUpdateSensorTree("/xyz/openbmc_project/sensors", sensorInterfaces);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800183
Scron Chang2703b022021-07-06 15:47:45 +0800184#ifdef FEATURE_HYBRID_SENSORS
185
186 if (!ipmi::sensor::sensors.empty())
187 {
188 for (const auto& sensor : ipmi::sensor::sensors)
189 {
190 // Threshold sensors should not be emplaced in here.
George Liuc024b392025-08-21 16:38:58 +0800191 if (sensor.second.sensorPath.starts_with(
192 "/xyz/openbmc_project/sensors/"))
Scron Chang2703b022021-07-06 15:47:45 +0800193 {
194 continue;
195 }
196
197 // The bus service name is not listed in ipmi::sensor::Info. Give it
198 // an empty string. For those function using non-threshold sensors,
199 // the bus service name will be retrieved in an alternative way.
200 boost::container::flat_map<std::string, std::vector<std::string>>
201 connectionMap{
202 {"", {sensor.second.propertyInterfaces.begin()->first}}};
203 sensorTreePtr->emplace(sensor.second.sensorPath, connectionMap);
204 }
205 }
206
207#endif
208
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800209 // Error if searching for sensors failed.
210 if (!sensorRez)
Willy Tude54f482021-01-26 15:59:09 -0800211 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800212 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800213 }
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800214
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700215 filterSensors(*sensorTreePtr);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800216 // Add VR control as optional search path.
217 (void)lbdUpdateSensorTree("/xyz/openbmc_project/vr", vrInterfaces);
218
Willy Tude54f482021-01-26 15:59:09 -0800219 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800220 sensorUpdatedIndex++;
Josh Lehana55c9532020-10-28 21:59:06 -0700221 // The SDR is being regenerated, wipe the old stats
222 sdrStatsTable.wipeTable();
Jie Yangf0a89942021-07-29 15:30:25 -0700223 sdrWriteTable.wipeTable();
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800224 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800225}
226
227bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
228{
229 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
230 bool sensorNumMapUpated = false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800231 static uint16_t prevSensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -0800232 std::shared_ptr<SensorSubTree> sensorTree;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800233 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
Willy Tude54f482021-01-26 15:59:09 -0800234 if (!sensorTree)
235 {
236 return sensorNumMapUpated;
237 }
238
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800239 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
Willy Tude54f482021-01-26 15:59:09 -0800240 {
241 sensorNumMap = sensorNumMapPtr;
242 return sensorNumMapUpated;
243 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800244 prevSensorUpdatedIndex = curSensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800245
246 sensorNumMapPtr = std::make_shared<SensorNumMap>();
247
248 uint16_t sensorNum = 0;
249 uint16_t sensorIndex = 0;
250 for (const auto& sensor : *sensorTree)
251 {
252 sensorNumMapPtr->insert(
253 SensorNumMap::value_type(sensorNum, sensor.first));
254 sensorIndex++;
255 if (sensorIndex == maxSensorsPerLUN)
256 {
257 sensorIndex = lun1Sensor0;
258 }
259 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
260 {
261 // Skip assigning LUN 0x2 any sensors
262 sensorIndex = lun3Sensor0;
263 }
264 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
265 {
266 // this is an error, too many IPMI sensors
267 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
268 }
269 sensorNum = sensorIndex;
270 }
271 sensorNumMap = sensorNumMapPtr;
272 sensorNumMapUpated = true;
273 return sensorNumMapUpated;
274}
275} // namespace details
276
277bool getSensorSubtree(SensorSubTree& subtree)
278{
279 std::shared_ptr<SensorSubTree> sensorTree;
280 details::getSensorSubtree(sensorTree);
281 if (!sensorTree)
282 {
283 return false;
284 }
285
286 subtree = *sensorTree;
287 return true;
288}
289
Scron Chang2703b022021-07-06 15:47:45 +0800290#ifdef FEATURE_HYBRID_SENSORS
291// Static sensors are listed in sensor-gen.cpp.
Patrick Williams69b4c282025-03-03 11:19:13 -0500292ipmi::sensor::IdInfoMap::const_iterator findStaticSensor(
293 const std::string& path)
Scron Chang2703b022021-07-06 15:47:45 +0800294{
295 return std::find_if(
296 ipmi::sensor::sensors.begin(), ipmi::sensor::sensors.end(),
297 [&path](const ipmi::sensor::IdInfoMap::value_type& findSensor) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400298 return findSensor.second.sensorPath == path;
299 });
Scron Chang2703b022021-07-06 15:47:45 +0800300}
301#endif
302
Willy Tude54f482021-01-26 15:59:09 -0800303std::string getSensorTypeStringFromPath(const std::string& path)
304{
305 // get sensor type string from path, path is defined as
306 // /xyz/openbmc_project/sensors/<type>/label
307 size_t typeEnd = path.rfind("/");
308 if (typeEnd == std::string::npos)
309 {
310 return path;
311 }
312 size_t typeStart = path.rfind("/", typeEnd - 1);
313 if (typeStart == std::string::npos)
314 {
315 return path;
316 }
317 // Start at the character after the '/'
318 typeStart++;
319 return path.substr(typeStart, typeEnd - typeStart);
320}
321
322uint8_t getSensorTypeFromPath(const std::string& path)
323{
324 uint8_t sensorType = 0;
325 std::string type = getSensorTypeStringFromPath(path);
326 auto findSensor = sensorTypes.find(type.c_str());
327 if (findSensor != sensorTypes.end())
328 {
Scron Chang2b42d7e2021-07-06 15:45:47 +0800329 sensorType =
330 static_cast<uint8_t>(std::get<sensorTypeCodes>(findSensor->second));
Willy Tude54f482021-01-26 15:59:09 -0800331 } // else default 0x0 RESERVED
332
333 return sensorType;
334}
335
336uint16_t getSensorNumberFromPath(const std::string& path)
337{
338 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
339 details::getSensorNumMap(sensorNumMapPtr);
340 if (!sensorNumMapPtr)
341 {
342 return invalidSensorNumber;
343 }
344
345 try
346 {
347 return sensorNumMapPtr->right.at(path);
348 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500349 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800350 {
Willy Tude54f482021-01-26 15:59:09 -0800351 return invalidSensorNumber;
352 }
353}
354
355uint8_t getSensorEventTypeFromPath(const std::string& path)
356{
Scron Chang2b42d7e2021-07-06 15:45:47 +0800357 uint8_t sensorEventType = 0;
358 std::string type = getSensorTypeStringFromPath(path);
359 auto findSensor = sensorTypes.find(type.c_str());
360 if (findSensor != sensorTypes.end())
361 {
362 sensorEventType = static_cast<uint8_t>(
363 std::get<sensorEventTypeCodes>(findSensor->second));
364 }
365
366 return sensorEventType;
Willy Tude54f482021-01-26 15:59:09 -0800367}
368
369std::string getPathFromSensorNumber(uint16_t sensorNum)
370{
371 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
372 details::getSensorNumMap(sensorNumMapPtr);
373 if (!sensorNumMapPtr)
374 {
375 return std::string();
376 }
377
378 try
379 {
380 return sensorNumMapPtr->left.at(sensorNum);
381 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500382 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800383 {
Willy Tude54f482021-01-26 15:59:09 -0800384 return std::string();
385 }
386}
387
388namespace ipmi
389{
390
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100391std::optional<std::map<std::string, std::vector<std::string>>>
Willy Tude54f482021-01-26 15:59:09 -0800392 getObjectInterfaces(const char* path)
393{
394 std::map<std::string, std::vector<std::string>> interfacesResponse;
395 std::vector<std::string> interfaces;
396 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
397
Alexander Hansen93aa9832025-11-07 15:29:49 +0100398 sdbusplus::message_t getObjectMessage = dbus->new_method_call(
399 ObjectMapper::default_service, ObjectMapper::instance_path,
400 ObjectMapper::interface, ObjectMapper::method_names::get_object);
Willy Tude54f482021-01-26 15:59:09 -0800401 getObjectMessage.append(path, interfaces);
402
403 try
404 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500405 sdbusplus::message_t response = dbus->call(getObjectMessage);
Willy Tude54f482021-01-26 15:59:09 -0800406 response.read(interfacesResponse);
407 }
408 catch (const std::exception& e)
409 {
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100410 return std::nullopt;
Willy Tude54f482021-01-26 15:59:09 -0800411 }
412
413 return interfacesResponse;
414}
415
Patrick Williams69b4c282025-03-03 11:19:13 -0500416std::map<std::string, Value> getEntityManagerProperties(const char* path,
417 const char* interface)
Willy Tude54f482021-01-26 15:59:09 -0800418{
419 std::map<std::string, Value> properties;
420 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
421
Patrick Williams5d82f472022-07-22 19:26:53 -0500422 sdbusplus::message_t getProperties =
Willy Tude54f482021-01-26 15:59:09 -0800423 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
424 "org.freedesktop.DBus.Properties", "GetAll");
425 getProperties.append(interface);
426
427 try
428 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500429 sdbusplus::message_t response = dbus->call(getProperties);
Willy Tude54f482021-01-26 15:59:09 -0800430 response.read(properties);
431 }
432 catch (const std::exception& e)
433 {
George Liude6694e2024-07-17 15:22:25 +0800434 lg2::error("Failed to GetAll, path: {PATH}, interface: {INTERFACE}, "
435 "error: {ERROR}",
436 "PATH", path, "INTERFACE", interface, "ERROR", e);
Willy Tude54f482021-01-26 15:59:09 -0800437 }
438
439 return properties;
440}
441
Willy Tu4eca2512022-06-20 21:14:51 -0700442// Fetch the ipmiDecoratorPaths to get the list of dbus objects that
443// have ipmi decorator to prevent unnessary dbus call to fetch the info
Patrick Williams69b4c282025-03-03 11:19:13 -0500444std::optional<std::unordered_set<std::string>>& getIpmiDecoratorPaths(
445 const std::optional<ipmi::Context::ptr>& ctx)
Willy Tu4eca2512022-06-20 21:14:51 -0700446{
447 static std::optional<std::unordered_set<std::string>> ipmiDecoratorPaths;
448
449 if (!ctx.has_value() || ipmiDecoratorPaths != std::nullopt)
450 {
451 return ipmiDecoratorPaths;
452 }
453
George Liude1420d2025-03-03 15:14:25 +0800454 using Paths = std::vector<std::string>;
Willy Tu4eca2512022-06-20 21:14:51 -0700455 boost::system::error_code ec;
George Liude1420d2025-03-03 15:14:25 +0800456 Paths paths = ipmi::callDbusMethod<Paths>(
Alexander Hansen93aa9832025-11-07 15:29:49 +0100457 *ctx, ec, ObjectMapper::default_service, ObjectMapper::instance_path,
458 ObjectMapper::interface, ObjectMapper::method_names::get_sub_tree_paths,
459 "/", int32_t(0),
George Liude1420d2025-03-03 15:14:25 +0800460 std::array<const char*, 1>{
461 "xyz.openbmc_project.Inventory.Decorator.Ipmi"});
462
Willy Tu4eca2512022-06-20 21:14:51 -0700463 if (ec)
464 {
465 return ipmiDecoratorPaths;
466 }
467
Patrick Williams1318a5e2024-08-16 15:19:54 -0400468 ipmiDecoratorPaths =
469 std::unordered_set<std::string>(paths.begin(), paths.end());
Willy Tu4eca2512022-06-20 21:14:51 -0700470 return ipmiDecoratorPaths;
471}
472
Willy Tude54f482021-01-26 15:59:09 -0800473const std::string* getSensorConfigurationInterface(
474 const std::map<std::string, std::vector<std::string>>&
475 sensorInterfacesResponse)
476{
477 auto entityManagerService =
478 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
479 if (entityManagerService == sensorInterfacesResponse.end())
480 {
481 return nullptr;
482 }
483
484 // Find the fan configuration first (fans can have multiple configuration
485 // interfaces).
486 for (const auto& entry : entityManagerService->second)
487 {
488 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
489 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
490 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
491 {
492 return &entry;
493 }
494 }
495
496 for (const auto& entry : entityManagerService->second)
497 {
George Liuc024b392025-08-21 16:38:58 +0800498 if (entry.starts_with("xyz.openbmc_project.Configuration."))
Willy Tude54f482021-01-26 15:59:09 -0800499 {
500 return &entry;
501 }
502 }
503
504 return nullptr;
505}
506
507// Follow Association properties for Sensor back to the Board dbus object to
508// check for an EntityId and EntityInstance property.
Willy Tu4eca2512022-06-20 21:14:51 -0700509void updateIpmiFromAssociation(
510 const std::string& path,
511 const std::unordered_set<std::string>& ipmiDecoratorPaths,
512 const DbusInterfaceMap& sensorMap, uint8_t& entityId,
513 uint8_t& entityInstance)
Willy Tude54f482021-01-26 15:59:09 -0800514{
515 namespace fs = std::filesystem;
516
517 auto sensorAssociationObject =
518 sensorMap.find("xyz.openbmc_project.Association.Definitions");
519 if (sensorAssociationObject == sensorMap.end())
520 {
521 if constexpr (debug)
522 {
523 std::fprintf(stderr, "path=%s, no association interface found\n",
524 path.c_str());
525 }
526
527 return;
528 }
529
530 auto associationObject =
531 sensorAssociationObject->second.find("Associations");
532 if (associationObject == sensorAssociationObject->second.end())
533 {
534 if constexpr (debug)
535 {
536 std::fprintf(stderr, "path=%s, no association records found\n",
537 path.c_str());
538 }
539
540 return;
541 }
542
543 std::vector<Association> associationValues =
544 std::get<std::vector<Association>>(associationObject->second);
545
546 // loop through the Associations looking for the right one:
547 for (const auto& entry : associationValues)
548 {
549 // forward, reverse, endpoint
550 const std::string& forward = std::get<0>(entry);
551 const std::string& reverse = std::get<1>(entry);
552 const std::string& endpoint = std::get<2>(entry);
553
554 // We only currently concern ourselves with chassis+all_sensors.
555 if (!(forward == "chassis" && reverse == "all_sensors"))
556 {
557 continue;
558 }
559
560 // the endpoint is the board entry provided by
561 // Entity-Manager. so let's grab its properties if it has
562 // the right interface.
563
564 // just try grabbing the properties first.
Willy Tu4eca2512022-06-20 21:14:51 -0700565 ipmi::PropertyMap::iterator entityIdProp;
566 ipmi::PropertyMap::iterator entityInstanceProp;
567 if (ipmiDecoratorPaths.contains(endpoint))
568 {
569 std::map<std::string, Value> ipmiProperties =
570 getEntityManagerProperties(
571 endpoint.c_str(),
572 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
Willy Tude54f482021-01-26 15:59:09 -0800573
Willy Tu4eca2512022-06-20 21:14:51 -0700574 entityIdProp = ipmiProperties.find("EntityId");
575 entityInstanceProp = ipmiProperties.find("EntityInstance");
576 if (entityIdProp != ipmiProperties.end())
577 {
578 entityId = static_cast<uint8_t>(
579 std::get<uint64_t>(entityIdProp->second));
580 }
581 if (entityInstanceProp != ipmiProperties.end())
582 {
583 entityInstance = static_cast<uint8_t>(
584 std::get<uint64_t>(entityInstanceProp->second));
585 }
Willy Tude54f482021-01-26 15:59:09 -0800586 }
587
588 // Now check the entity-manager entry for this sensor to see
589 // if it has its own value and use that instead.
590 //
591 // In theory, checking this first saves us from checking
592 // both, except in most use-cases identified, there won't be
593 // a per sensor override, so we need to always check both.
594 std::string sensorNameFromPath = fs::path(path).filename();
595
596 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
597
598 // Download the interfaces for the sensor from
599 // Entity-Manager to find the name of the configuration
600 // interface.
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100601 std::optional<std::map<std::string, std::vector<std::string>>>
602 sensorInterfacesResponseOpt =
Willy Tude54f482021-01-26 15:59:09 -0800603 getObjectInterfaces(sensorConfigPath.c_str());
604
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100605 if (!sensorInterfacesResponseOpt.has_value())
606 {
George Liude6694e2024-07-17 15:22:25 +0800607 lg2::debug("Failed to GetObject, path: {PATH}", "PATH",
608 sensorConfigPath);
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100609 continue;
610 }
611
Willy Tude54f482021-01-26 15:59:09 -0800612 const std::string* configurationInterface =
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100613 getSensorConfigurationInterface(
614 sensorInterfacesResponseOpt.value());
Willy Tude54f482021-01-26 15:59:09 -0800615
Harvey Wu7bb84db2023-06-26 10:02:08 +0800616 // If there are multi association path settings and only one path exist,
617 // we need to continue if cannot find configuration interface for this
618 // sensor.
Willy Tude54f482021-01-26 15:59:09 -0800619 if (!configurationInterface)
620 {
Harvey Wu7bb84db2023-06-26 10:02:08 +0800621 continue;
Willy Tude54f482021-01-26 15:59:09 -0800622 }
623
624 // We found a configuration interface.
625 std::map<std::string, Value> configurationProperties =
626 getEntityManagerProperties(sensorConfigPath.c_str(),
627 configurationInterface->c_str());
628
629 entityIdProp = configurationProperties.find("EntityId");
630 entityInstanceProp = configurationProperties.find("EntityInstance");
631 if (entityIdProp != configurationProperties.end())
632 {
633 entityId =
634 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
635 }
636 if (entityInstanceProp != configurationProperties.end())
637 {
638 entityInstance = static_cast<uint8_t>(
639 std::get<uint64_t>(entityInstanceProp->second));
640 }
641
642 // stop searching Association records.
643 break;
644 } // end for Association vectors.
645
646 if constexpr (debug)
647 {
648 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
649 path.c_str(), entityId, entityInstance);
650 }
651}
652
653} // namespace ipmi