blob: 6065edb86f9f4e141d83383ef24cd344bdf3b4ac [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 Hansen653aec02025-11-14 13:38:08 +010023#include <xyz/openbmc_project/Sensor/Threshold/Critical/common.hpp>
24#include <xyz/openbmc_project/Sensor/Threshold/Warning/common.hpp>
Alexander Hansenf365c7f2025-11-14 12:08:34 +010025#include <xyz/openbmc_project/Sensor/Value/common.hpp>
Johnathan Mantey04b0b072023-10-10 12:30:35 -070026
27#include <fstream>
Willy Tu4eca2512022-06-20 21:14:51 -070028#include <optional>
29#include <unordered_set>
30
Alexander Hansen93aa9832025-11-07 15:29:49 +010031using ObjectMapper = sdbusplus::common::xyz::openbmc_project::ObjectMapper;
Alexander Hansenf365c7f2025-11-14 12:08:34 +010032using SensorValue = sdbusplus::common::xyz::openbmc_project::sensor::Value;
Alexander Hansen653aec02025-11-14 13:38:08 +010033using SensorThresholdWarning =
34 sdbusplus::common::xyz::openbmc_project::sensor::threshold::Warning;
35using SensorThresholdCritical =
36 sdbusplus::common::xyz::openbmc_project::sensor::threshold::Critical;
Alexander Hansen93aa9832025-11-07 15:29:49 +010037
Scron Chang2703b022021-07-06 15:47:45 +080038#ifdef FEATURE_HYBRID_SENSORS
39
40#include <ipmid/utils.hpp>
41namespace ipmi
42{
43namespace sensor
44{
45extern const IdInfoMap sensors;
46} // namespace sensor
47} // namespace ipmi
48
49#endif
50
Johnathan Mantey34d19732024-07-17 07:59:54 -070051boost::container::flat_map<
52 const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr>
53 sensorTypes{
54 {{"temperature", std::make_pair(SensorTypeCodes::temperature,
55 SensorEventTypeCodes::threshold)},
56 {"voltage", std::make_pair(SensorTypeCodes::voltage,
57 SensorEventTypeCodes::threshold)},
58 {"current", std::make_pair(SensorTypeCodes::current,
59 SensorEventTypeCodes::threshold)},
60 {"fan_tach", std::make_pair(SensorTypeCodes::fan,
61 SensorEventTypeCodes::threshold)},
62 {"fan_pwm", std::make_pair(SensorTypeCodes::fan,
63 SensorEventTypeCodes::threshold)},
George Liuf2807ed2025-04-03 15:52:57 +080064 {"intrusion", std::make_pair(SensorTypeCodes::physicalSecurity,
Johnathan Mantey34d19732024-07-17 07:59:54 -070065 SensorEventTypeCodes::sensorSpecified)},
66 {"processor", std::make_pair(SensorTypeCodes::processor,
67 SensorEventTypeCodes::sensorSpecified)},
68 {"power", std::make_pair(SensorTypeCodes::other,
69 SensorEventTypeCodes::threshold)},
70 {"memory", std::make_pair(SensorTypeCodes::memory,
71 SensorEventTypeCodes::sensorSpecified)},
George Liuf2807ed2025-04-03 15:52:57 +080072 {"state", std::make_pair(SensorTypeCodes::powerUnit,
Johnathan Mantey34d19732024-07-17 07:59:54 -070073 SensorEventTypeCodes::sensorSpecified)},
74 {"buttons", std::make_pair(SensorTypeCodes::buttons,
75 SensorEventTypeCodes::sensorSpecified)},
76 {"watchdog", std::make_pair(SensorTypeCodes::watchdog2,
77 SensorEventTypeCodes::sensorSpecified)},
78 {"entity", std::make_pair(SensorTypeCodes::entity,
79 SensorEventTypeCodes::sensorSpecified)},
80 {"energy", std::make_pair(SensorTypeCodes::other,
81 SensorEventTypeCodes::threshold)}}};
82
Willy Tude54f482021-01-26 15:59:09 -080083namespace details
84{
Johnathan Mantey04b0b072023-10-10 12:30:35 -070085
86// IPMI supports a smaller number of sensors than are available via Redfish.
87// Trim the list of sensors, via a configuration file.
88// Read the IPMI Sensor Filtering section in docs/configuration.md for
89// a more detailed description.
90static void filterSensors(SensorSubTree& subtree)
91{
92 constexpr const char* filterFilename =
93 "/usr/share/ipmi-providers/sensor_filter.json";
94 std::ifstream filterFile(filterFilename);
95 if (!filterFile.good())
96 {
97 return;
98 }
Patrick Williams1318a5e2024-08-16 15:19:54 -040099 nlohmann::json sensorFilterJSON =
100 nlohmann::json::parse(filterFile, nullptr, false);
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700101 nlohmann::json::iterator svcFilterit =
102 sensorFilterJSON.find("ServiceFilter");
103 if (svcFilterit == sensorFilterJSON.end())
104 {
105 return;
106 }
107
108 subtree.erase(std::remove_if(subtree.begin(), subtree.end(),
109 [svcFilterit](SensorSubTree::value_type& kv) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400110 auto& [_, serviceToIfaces] = kv;
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700111
Patrick Williams1318a5e2024-08-16 15:19:54 -0400112 for (auto service = svcFilterit->begin();
113 service != svcFilterit->end();
114 ++service)
115 {
116 serviceToIfaces.erase(*service);
117 }
118 return serviceToIfaces.empty();
119 }),
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700120 subtree.end());
121}
122
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800123uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
Willy Tude54f482021-01-26 15:59:09 -0800124{
125 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800126 static uint16_t sensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -0800127 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williams5d82f472022-07-22 19:26:53 -0500128 static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800129 *dbus,
130 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
131 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500132 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Willy Tude54f482021-01-26 15:59:09 -0800133
Patrick Williams5d82f472022-07-22 19:26:53 -0500134 static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800135 *dbus,
136 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
137 "openbmc_project/sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500138 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Willy Tude54f482021-01-26 15:59:09 -0800139
Willy Tude54f482021-01-26 15:59:09 -0800140 if (sensorTreePtr)
141 {
142 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800143 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800144 }
145
146 sensorTreePtr = std::make_shared<SensorSubTree>();
147
Willy Tude54f482021-01-26 15:59:09 -0800148 static constexpr const int32_t depth = 2;
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800149
Patrick Williams369824e2023-10-20 11:18:23 -0500150 auto lbdUpdateSensorTree = [&dbus](const char* path,
151 const auto& interfaces) {
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800152 auto mapperCall = dbus->new_method_call(
Alexander Hansen93aa9832025-11-07 15:29:49 +0100153 ObjectMapper::default_service, ObjectMapper::instance_path,
154 ObjectMapper::interface, ObjectMapper::method_names::get_sub_tree);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800155 SensorSubTree sensorTreePartial;
156
157 mapperCall.append(path, depth, interfaces);
158
159 try
160 {
161 auto mapperReply = dbus->call(mapperCall);
162 mapperReply.read(sensorTreePartial);
163 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500164 catch (const sdbusplus::exception_t& e)
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800165 {
George Liude6694e2024-07-17 15:22:25 +0800166 lg2::error("Failed to update subtree, path: {PATH}, error: {ERROR}",
167 "PATH", path, "ERROR", e);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800168 return false;
169 }
170 if constexpr (debug)
171 {
172 std::fprintf(stderr, "IPMI updated: %zu sensors under %s\n",
173 sensorTreePartial.size(), path);
174 }
175 sensorTreePtr->merge(std::move(sensorTreePartial));
176 return true;
177 };
178
179 // Add sensors to SensorTree
180 static constexpr const std::array sensorInterfaces = {
Alexander Hansenf365c7f2025-11-14 12:08:34 +0100181 SensorValue::interface, "xyz.openbmc_project.Sensor.ValueMutability",
Alexander Hansen653aec02025-11-14 13:38:08 +0100182 SensorThresholdWarning::interface, SensorThresholdCritical::interface};
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800183 static constexpr const std::array vrInterfaces = {
184 "xyz.openbmc_project.Control.VoltageRegulatorMode"};
Willy Tude54f482021-01-26 15:59:09 -0800185
Patrick Williams1318a5e2024-08-16 15:19:54 -0400186 bool sensorRez =
187 lbdUpdateSensorTree("/xyz/openbmc_project/sensors", sensorInterfaces);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800188
Scron Chang2703b022021-07-06 15:47:45 +0800189#ifdef FEATURE_HYBRID_SENSORS
190
191 if (!ipmi::sensor::sensors.empty())
192 {
193 for (const auto& sensor : ipmi::sensor::sensors)
194 {
195 // Threshold sensors should not be emplaced in here.
George Liuc024b392025-08-21 16:38:58 +0800196 if (sensor.second.sensorPath.starts_with(
197 "/xyz/openbmc_project/sensors/"))
Scron Chang2703b022021-07-06 15:47:45 +0800198 {
199 continue;
200 }
201
202 // The bus service name is not listed in ipmi::sensor::Info. Give it
203 // an empty string. For those function using non-threshold sensors,
204 // the bus service name will be retrieved in an alternative way.
205 boost::container::flat_map<std::string, std::vector<std::string>>
206 connectionMap{
207 {"", {sensor.second.propertyInterfaces.begin()->first}}};
208 sensorTreePtr->emplace(sensor.second.sensorPath, connectionMap);
209 }
210 }
211
212#endif
213
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800214 // Error if searching for sensors failed.
215 if (!sensorRez)
Willy Tude54f482021-01-26 15:59:09 -0800216 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800217 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800218 }
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800219
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700220 filterSensors(*sensorTreePtr);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800221 // Add VR control as optional search path.
222 (void)lbdUpdateSensorTree("/xyz/openbmc_project/vr", vrInterfaces);
223
Willy Tude54f482021-01-26 15:59:09 -0800224 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800225 sensorUpdatedIndex++;
Josh Lehana55c9532020-10-28 21:59:06 -0700226 // The SDR is being regenerated, wipe the old stats
227 sdrStatsTable.wipeTable();
Jie Yangf0a89942021-07-29 15:30:25 -0700228 sdrWriteTable.wipeTable();
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800229 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800230}
231
232bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
233{
234 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
235 bool sensorNumMapUpated = false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800236 static uint16_t prevSensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -0800237 std::shared_ptr<SensorSubTree> sensorTree;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800238 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
Willy Tude54f482021-01-26 15:59:09 -0800239 if (!sensorTree)
240 {
241 return sensorNumMapUpated;
242 }
243
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800244 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
Willy Tude54f482021-01-26 15:59:09 -0800245 {
246 sensorNumMap = sensorNumMapPtr;
247 return sensorNumMapUpated;
248 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800249 prevSensorUpdatedIndex = curSensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800250
251 sensorNumMapPtr = std::make_shared<SensorNumMap>();
252
253 uint16_t sensorNum = 0;
254 uint16_t sensorIndex = 0;
255 for (const auto& sensor : *sensorTree)
256 {
257 sensorNumMapPtr->insert(
258 SensorNumMap::value_type(sensorNum, sensor.first));
259 sensorIndex++;
260 if (sensorIndex == maxSensorsPerLUN)
261 {
262 sensorIndex = lun1Sensor0;
263 }
264 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
265 {
266 // Skip assigning LUN 0x2 any sensors
267 sensorIndex = lun3Sensor0;
268 }
269 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
270 {
271 // this is an error, too many IPMI sensors
272 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
273 }
274 sensorNum = sensorIndex;
275 }
276 sensorNumMap = sensorNumMapPtr;
277 sensorNumMapUpated = true;
278 return sensorNumMapUpated;
279}
280} // namespace details
281
282bool getSensorSubtree(SensorSubTree& subtree)
283{
284 std::shared_ptr<SensorSubTree> sensorTree;
285 details::getSensorSubtree(sensorTree);
286 if (!sensorTree)
287 {
288 return false;
289 }
290
291 subtree = *sensorTree;
292 return true;
293}
294
Scron Chang2703b022021-07-06 15:47:45 +0800295#ifdef FEATURE_HYBRID_SENSORS
296// Static sensors are listed in sensor-gen.cpp.
Patrick Williams69b4c282025-03-03 11:19:13 -0500297ipmi::sensor::IdInfoMap::const_iterator findStaticSensor(
298 const std::string& path)
Scron Chang2703b022021-07-06 15:47:45 +0800299{
300 return std::find_if(
301 ipmi::sensor::sensors.begin(), ipmi::sensor::sensors.end(),
302 [&path](const ipmi::sensor::IdInfoMap::value_type& findSensor) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400303 return findSensor.second.sensorPath == path;
304 });
Scron Chang2703b022021-07-06 15:47:45 +0800305}
306#endif
307
Willy Tude54f482021-01-26 15:59:09 -0800308std::string getSensorTypeStringFromPath(const std::string& path)
309{
310 // get sensor type string from path, path is defined as
311 // /xyz/openbmc_project/sensors/<type>/label
312 size_t typeEnd = path.rfind("/");
313 if (typeEnd == std::string::npos)
314 {
315 return path;
316 }
317 size_t typeStart = path.rfind("/", typeEnd - 1);
318 if (typeStart == std::string::npos)
319 {
320 return path;
321 }
322 // Start at the character after the '/'
323 typeStart++;
324 return path.substr(typeStart, typeEnd - typeStart);
325}
326
327uint8_t getSensorTypeFromPath(const std::string& path)
328{
329 uint8_t sensorType = 0;
330 std::string type = getSensorTypeStringFromPath(path);
331 auto findSensor = sensorTypes.find(type.c_str());
332 if (findSensor != sensorTypes.end())
333 {
Scron Chang2b42d7e2021-07-06 15:45:47 +0800334 sensorType =
335 static_cast<uint8_t>(std::get<sensorTypeCodes>(findSensor->second));
Willy Tude54f482021-01-26 15:59:09 -0800336 } // else default 0x0 RESERVED
337
338 return sensorType;
339}
340
341uint16_t getSensorNumberFromPath(const std::string& path)
342{
343 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
344 details::getSensorNumMap(sensorNumMapPtr);
345 if (!sensorNumMapPtr)
346 {
347 return invalidSensorNumber;
348 }
349
350 try
351 {
352 return sensorNumMapPtr->right.at(path);
353 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500354 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800355 {
Willy Tude54f482021-01-26 15:59:09 -0800356 return invalidSensorNumber;
357 }
358}
359
360uint8_t getSensorEventTypeFromPath(const std::string& path)
361{
Scron Chang2b42d7e2021-07-06 15:45:47 +0800362 uint8_t sensorEventType = 0;
363 std::string type = getSensorTypeStringFromPath(path);
364 auto findSensor = sensorTypes.find(type.c_str());
365 if (findSensor != sensorTypes.end())
366 {
367 sensorEventType = static_cast<uint8_t>(
368 std::get<sensorEventTypeCodes>(findSensor->second));
369 }
370
371 return sensorEventType;
Willy Tude54f482021-01-26 15:59:09 -0800372}
373
374std::string getPathFromSensorNumber(uint16_t sensorNum)
375{
376 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
377 details::getSensorNumMap(sensorNumMapPtr);
378 if (!sensorNumMapPtr)
379 {
380 return std::string();
381 }
382
383 try
384 {
385 return sensorNumMapPtr->left.at(sensorNum);
386 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500387 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800388 {
Willy Tude54f482021-01-26 15:59:09 -0800389 return std::string();
390 }
391}
392
393namespace ipmi
394{
395
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100396std::optional<std::map<std::string, std::vector<std::string>>>
Willy Tude54f482021-01-26 15:59:09 -0800397 getObjectInterfaces(const char* path)
398{
399 std::map<std::string, std::vector<std::string>> interfacesResponse;
400 std::vector<std::string> interfaces;
401 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
402
Alexander Hansen93aa9832025-11-07 15:29:49 +0100403 sdbusplus::message_t getObjectMessage = dbus->new_method_call(
404 ObjectMapper::default_service, ObjectMapper::instance_path,
405 ObjectMapper::interface, ObjectMapper::method_names::get_object);
Willy Tude54f482021-01-26 15:59:09 -0800406 getObjectMessage.append(path, interfaces);
407
408 try
409 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500410 sdbusplus::message_t response = dbus->call(getObjectMessage);
Willy Tude54f482021-01-26 15:59:09 -0800411 response.read(interfacesResponse);
412 }
413 catch (const std::exception& e)
414 {
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100415 return std::nullopt;
Willy Tude54f482021-01-26 15:59:09 -0800416 }
417
418 return interfacesResponse;
419}
420
Patrick Williams69b4c282025-03-03 11:19:13 -0500421std::map<std::string, Value> getEntityManagerProperties(const char* path,
422 const char* interface)
Willy Tude54f482021-01-26 15:59:09 -0800423{
424 std::map<std::string, Value> properties;
425 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
426
Patrick Williams5d82f472022-07-22 19:26:53 -0500427 sdbusplus::message_t getProperties =
Willy Tude54f482021-01-26 15:59:09 -0800428 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
429 "org.freedesktop.DBus.Properties", "GetAll");
430 getProperties.append(interface);
431
432 try
433 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500434 sdbusplus::message_t response = dbus->call(getProperties);
Willy Tude54f482021-01-26 15:59:09 -0800435 response.read(properties);
436 }
437 catch (const std::exception& e)
438 {
George Liude6694e2024-07-17 15:22:25 +0800439 lg2::error("Failed to GetAll, path: {PATH}, interface: {INTERFACE}, "
440 "error: {ERROR}",
441 "PATH", path, "INTERFACE", interface, "ERROR", e);
Willy Tude54f482021-01-26 15:59:09 -0800442 }
443
444 return properties;
445}
446
Willy Tu4eca2512022-06-20 21:14:51 -0700447// Fetch the ipmiDecoratorPaths to get the list of dbus objects that
448// have ipmi decorator to prevent unnessary dbus call to fetch the info
Patrick Williams69b4c282025-03-03 11:19:13 -0500449std::optional<std::unordered_set<std::string>>& getIpmiDecoratorPaths(
450 const std::optional<ipmi::Context::ptr>& ctx)
Willy Tu4eca2512022-06-20 21:14:51 -0700451{
452 static std::optional<std::unordered_set<std::string>> ipmiDecoratorPaths;
453
454 if (!ctx.has_value() || ipmiDecoratorPaths != std::nullopt)
455 {
456 return ipmiDecoratorPaths;
457 }
458
George Liude1420d2025-03-03 15:14:25 +0800459 using Paths = std::vector<std::string>;
Willy Tu4eca2512022-06-20 21:14:51 -0700460 boost::system::error_code ec;
George Liude1420d2025-03-03 15:14:25 +0800461 Paths paths = ipmi::callDbusMethod<Paths>(
Alexander Hansen93aa9832025-11-07 15:29:49 +0100462 *ctx, ec, ObjectMapper::default_service, ObjectMapper::instance_path,
463 ObjectMapper::interface, ObjectMapper::method_names::get_sub_tree_paths,
464 "/", int32_t(0),
George Liude1420d2025-03-03 15:14:25 +0800465 std::array<const char*, 1>{
466 "xyz.openbmc_project.Inventory.Decorator.Ipmi"});
467
Willy Tu4eca2512022-06-20 21:14:51 -0700468 if (ec)
469 {
470 return ipmiDecoratorPaths;
471 }
472
Patrick Williams1318a5e2024-08-16 15:19:54 -0400473 ipmiDecoratorPaths =
474 std::unordered_set<std::string>(paths.begin(), paths.end());
Willy Tu4eca2512022-06-20 21:14:51 -0700475 return ipmiDecoratorPaths;
476}
477
Willy Tude54f482021-01-26 15:59:09 -0800478const std::string* getSensorConfigurationInterface(
479 const std::map<std::string, std::vector<std::string>>&
480 sensorInterfacesResponse)
481{
482 auto entityManagerService =
483 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
484 if (entityManagerService == sensorInterfacesResponse.end())
485 {
486 return nullptr;
487 }
488
489 // Find the fan configuration first (fans can have multiple configuration
490 // interfaces).
491 for (const auto& entry : entityManagerService->second)
492 {
493 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
494 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
495 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
496 {
497 return &entry;
498 }
499 }
500
501 for (const auto& entry : entityManagerService->second)
502 {
George Liuc024b392025-08-21 16:38:58 +0800503 if (entry.starts_with("xyz.openbmc_project.Configuration."))
Willy Tude54f482021-01-26 15:59:09 -0800504 {
505 return &entry;
506 }
507 }
508
509 return nullptr;
510}
511
512// Follow Association properties for Sensor back to the Board dbus object to
513// check for an EntityId and EntityInstance property.
Willy Tu4eca2512022-06-20 21:14:51 -0700514void updateIpmiFromAssociation(
515 const std::string& path,
516 const std::unordered_set<std::string>& ipmiDecoratorPaths,
517 const DbusInterfaceMap& sensorMap, uint8_t& entityId,
518 uint8_t& entityInstance)
Willy Tude54f482021-01-26 15:59:09 -0800519{
520 namespace fs = std::filesystem;
521
522 auto sensorAssociationObject =
523 sensorMap.find("xyz.openbmc_project.Association.Definitions");
524 if (sensorAssociationObject == sensorMap.end())
525 {
526 if constexpr (debug)
527 {
528 std::fprintf(stderr, "path=%s, no association interface found\n",
529 path.c_str());
530 }
531
532 return;
533 }
534
535 auto associationObject =
536 sensorAssociationObject->second.find("Associations");
537 if (associationObject == sensorAssociationObject->second.end())
538 {
539 if constexpr (debug)
540 {
541 std::fprintf(stderr, "path=%s, no association records found\n",
542 path.c_str());
543 }
544
545 return;
546 }
547
548 std::vector<Association> associationValues =
549 std::get<std::vector<Association>>(associationObject->second);
550
551 // loop through the Associations looking for the right one:
552 for (const auto& entry : associationValues)
553 {
554 // forward, reverse, endpoint
555 const std::string& forward = std::get<0>(entry);
556 const std::string& reverse = std::get<1>(entry);
557 const std::string& endpoint = std::get<2>(entry);
558
559 // We only currently concern ourselves with chassis+all_sensors.
560 if (!(forward == "chassis" && reverse == "all_sensors"))
561 {
562 continue;
563 }
564
565 // the endpoint is the board entry provided by
566 // Entity-Manager. so let's grab its properties if it has
567 // the right interface.
568
569 // just try grabbing the properties first.
Willy Tu4eca2512022-06-20 21:14:51 -0700570 ipmi::PropertyMap::iterator entityIdProp;
571 ipmi::PropertyMap::iterator entityInstanceProp;
572 if (ipmiDecoratorPaths.contains(endpoint))
573 {
574 std::map<std::string, Value> ipmiProperties =
575 getEntityManagerProperties(
576 endpoint.c_str(),
577 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
Willy Tude54f482021-01-26 15:59:09 -0800578
Willy Tu4eca2512022-06-20 21:14:51 -0700579 entityIdProp = ipmiProperties.find("EntityId");
580 entityInstanceProp = ipmiProperties.find("EntityInstance");
581 if (entityIdProp != ipmiProperties.end())
582 {
583 entityId = static_cast<uint8_t>(
584 std::get<uint64_t>(entityIdProp->second));
585 }
586 if (entityInstanceProp != ipmiProperties.end())
587 {
588 entityInstance = static_cast<uint8_t>(
589 std::get<uint64_t>(entityInstanceProp->second));
590 }
Willy Tude54f482021-01-26 15:59:09 -0800591 }
592
593 // Now check the entity-manager entry for this sensor to see
594 // if it has its own value and use that instead.
595 //
596 // In theory, checking this first saves us from checking
597 // both, except in most use-cases identified, there won't be
598 // a per sensor override, so we need to always check both.
599 std::string sensorNameFromPath = fs::path(path).filename();
600
601 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
602
603 // Download the interfaces for the sensor from
604 // Entity-Manager to find the name of the configuration
605 // interface.
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100606 std::optional<std::map<std::string, std::vector<std::string>>>
607 sensorInterfacesResponseOpt =
Willy Tude54f482021-01-26 15:59:09 -0800608 getObjectInterfaces(sensorConfigPath.c_str());
609
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100610 if (!sensorInterfacesResponseOpt.has_value())
611 {
George Liude6694e2024-07-17 15:22:25 +0800612 lg2::debug("Failed to GetObject, path: {PATH}", "PATH",
613 sensorConfigPath);
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100614 continue;
615 }
616
Willy Tude54f482021-01-26 15:59:09 -0800617 const std::string* configurationInterface =
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100618 getSensorConfigurationInterface(
619 sensorInterfacesResponseOpt.value());
Willy Tude54f482021-01-26 15:59:09 -0800620
Harvey Wu7bb84db2023-06-26 10:02:08 +0800621 // If there are multi association path settings and only one path exist,
622 // we need to continue if cannot find configuration interface for this
623 // sensor.
Willy Tude54f482021-01-26 15:59:09 -0800624 if (!configurationInterface)
625 {
Harvey Wu7bb84db2023-06-26 10:02:08 +0800626 continue;
Willy Tude54f482021-01-26 15:59:09 -0800627 }
628
629 // We found a configuration interface.
630 std::map<std::string, Value> configurationProperties =
631 getEntityManagerProperties(sensorConfigPath.c_str(),
632 configurationInterface->c_str());
633
634 entityIdProp = configurationProperties.find("EntityId");
635 entityInstanceProp = configurationProperties.find("EntityInstance");
636 if (entityIdProp != configurationProperties.end())
637 {
638 entityId =
639 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
640 }
641 if (entityInstanceProp != configurationProperties.end())
642 {
643 entityInstance = static_cast<uint8_t>(
644 std::get<uint64_t>(entityInstanceProp->second));
645 }
646
647 // stop searching Association records.
648 break;
649 } // end for Association vectors.
650
651 if constexpr (debug)
652 {
653 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
654 path.c_str(), entityId, entityInstance);
655 }
656}
657
658} // namespace ipmi