blob: a544dc11c8e0a8933aec084b4bfd8d586363f76f [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>
Johnathan Mantey04b0b072023-10-10 12:30:35 -070023
24#include <fstream>
Willy Tu4eca2512022-06-20 21:14:51 -070025#include <optional>
26#include <unordered_set>
27
Alexander Hansen93aa9832025-11-07 15:29:49 +010028using ObjectMapper = sdbusplus::common::xyz::openbmc_project::ObjectMapper;
29
Scron Chang2703b022021-07-06 15:47:45 +080030#ifdef FEATURE_HYBRID_SENSORS
31
32#include <ipmid/utils.hpp>
33namespace ipmi
34{
35namespace sensor
36{
37extern const IdInfoMap sensors;
38} // namespace sensor
39} // namespace ipmi
40
41#endif
42
Johnathan Mantey34d19732024-07-17 07:59:54 -070043boost::container::flat_map<
44 const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr>
45 sensorTypes{
46 {{"temperature", std::make_pair(SensorTypeCodes::temperature,
47 SensorEventTypeCodes::threshold)},
48 {"voltage", std::make_pair(SensorTypeCodes::voltage,
49 SensorEventTypeCodes::threshold)},
50 {"current", std::make_pair(SensorTypeCodes::current,
51 SensorEventTypeCodes::threshold)},
52 {"fan_tach", std::make_pair(SensorTypeCodes::fan,
53 SensorEventTypeCodes::threshold)},
54 {"fan_pwm", std::make_pair(SensorTypeCodes::fan,
55 SensorEventTypeCodes::threshold)},
George Liuf2807ed2025-04-03 15:52:57 +080056 {"intrusion", std::make_pair(SensorTypeCodes::physicalSecurity,
Johnathan Mantey34d19732024-07-17 07:59:54 -070057 SensorEventTypeCodes::sensorSpecified)},
58 {"processor", std::make_pair(SensorTypeCodes::processor,
59 SensorEventTypeCodes::sensorSpecified)},
60 {"power", std::make_pair(SensorTypeCodes::other,
61 SensorEventTypeCodes::threshold)},
62 {"memory", std::make_pair(SensorTypeCodes::memory,
63 SensorEventTypeCodes::sensorSpecified)},
George Liuf2807ed2025-04-03 15:52:57 +080064 {"state", std::make_pair(SensorTypeCodes::powerUnit,
Johnathan Mantey34d19732024-07-17 07:59:54 -070065 SensorEventTypeCodes::sensorSpecified)},
66 {"buttons", std::make_pair(SensorTypeCodes::buttons,
67 SensorEventTypeCodes::sensorSpecified)},
68 {"watchdog", std::make_pair(SensorTypeCodes::watchdog2,
69 SensorEventTypeCodes::sensorSpecified)},
70 {"entity", std::make_pair(SensorTypeCodes::entity,
71 SensorEventTypeCodes::sensorSpecified)},
72 {"energy", std::make_pair(SensorTypeCodes::other,
73 SensorEventTypeCodes::threshold)}}};
74
Willy Tude54f482021-01-26 15:59:09 -080075namespace details
76{
Johnathan Mantey04b0b072023-10-10 12:30:35 -070077
78// IPMI supports a smaller number of sensors than are available via Redfish.
79// Trim the list of sensors, via a configuration file.
80// Read the IPMI Sensor Filtering section in docs/configuration.md for
81// a more detailed description.
82static void filterSensors(SensorSubTree& subtree)
83{
84 constexpr const char* filterFilename =
85 "/usr/share/ipmi-providers/sensor_filter.json";
86 std::ifstream filterFile(filterFilename);
87 if (!filterFile.good())
88 {
89 return;
90 }
Patrick Williams1318a5e2024-08-16 15:19:54 -040091 nlohmann::json sensorFilterJSON =
92 nlohmann::json::parse(filterFile, nullptr, false);
Johnathan Mantey04b0b072023-10-10 12:30:35 -070093 nlohmann::json::iterator svcFilterit =
94 sensorFilterJSON.find("ServiceFilter");
95 if (svcFilterit == sensorFilterJSON.end())
96 {
97 return;
98 }
99
100 subtree.erase(std::remove_if(subtree.begin(), subtree.end(),
101 [svcFilterit](SensorSubTree::value_type& kv) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400102 auto& [_, serviceToIfaces] = kv;
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700103
Patrick Williams1318a5e2024-08-16 15:19:54 -0400104 for (auto service = svcFilterit->begin();
105 service != svcFilterit->end();
106 ++service)
107 {
108 serviceToIfaces.erase(*service);
109 }
110 return serviceToIfaces.empty();
111 }),
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700112 subtree.end());
113}
114
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800115uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
Willy Tude54f482021-01-26 15:59:09 -0800116{
117 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800118 static uint16_t sensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -0800119 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
Patrick Williams5d82f472022-07-22 19:26:53 -0500120 static sdbusplus::bus::match_t sensorAdded(
Willy Tude54f482021-01-26 15:59:09 -0800121 *dbus,
122 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
123 "sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500124 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Willy Tude54f482021-01-26 15:59:09 -0800125
Patrick Williams5d82f472022-07-22 19:26:53 -0500126 static sdbusplus::bus::match_t sensorRemoved(
Willy Tude54f482021-01-26 15:59:09 -0800127 *dbus,
128 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
129 "openbmc_project/sensors/'",
Patrick Williams5d82f472022-07-22 19:26:53 -0500130 [](sdbusplus::message_t&) { sensorTreePtr.reset(); });
Willy Tude54f482021-01-26 15:59:09 -0800131
Willy Tude54f482021-01-26 15:59:09 -0800132 if (sensorTreePtr)
133 {
134 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800135 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800136 }
137
138 sensorTreePtr = std::make_shared<SensorSubTree>();
139
Willy Tude54f482021-01-26 15:59:09 -0800140 static constexpr const int32_t depth = 2;
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800141
Patrick Williams369824e2023-10-20 11:18:23 -0500142 auto lbdUpdateSensorTree = [&dbus](const char* path,
143 const auto& interfaces) {
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800144 auto mapperCall = dbus->new_method_call(
Alexander Hansen93aa9832025-11-07 15:29:49 +0100145 ObjectMapper::default_service, ObjectMapper::instance_path,
146 ObjectMapper::interface, ObjectMapper::method_names::get_sub_tree);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800147 SensorSubTree sensorTreePartial;
148
149 mapperCall.append(path, depth, interfaces);
150
151 try
152 {
153 auto mapperReply = dbus->call(mapperCall);
154 mapperReply.read(sensorTreePartial);
155 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500156 catch (const sdbusplus::exception_t& e)
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800157 {
George Liude6694e2024-07-17 15:22:25 +0800158 lg2::error("Failed to update subtree, path: {PATH}, error: {ERROR}",
159 "PATH", path, "ERROR", e);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800160 return false;
161 }
162 if constexpr (debug)
163 {
164 std::fprintf(stderr, "IPMI updated: %zu sensors under %s\n",
165 sensorTreePartial.size(), path);
166 }
167 sensorTreePtr->merge(std::move(sensorTreePartial));
168 return true;
169 };
170
171 // Add sensors to SensorTree
172 static constexpr const std::array sensorInterfaces = {
Willy Tude54f482021-01-26 15:59:09 -0800173 "xyz.openbmc_project.Sensor.Value",
Jie Yangf0a89942021-07-29 15:30:25 -0700174 "xyz.openbmc_project.Sensor.ValueMutability",
Willy Tude54f482021-01-26 15:59:09 -0800175 "xyz.openbmc_project.Sensor.Threshold.Warning",
176 "xyz.openbmc_project.Sensor.Threshold.Critical"};
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800177 static constexpr const std::array vrInterfaces = {
178 "xyz.openbmc_project.Control.VoltageRegulatorMode"};
Willy Tude54f482021-01-26 15:59:09 -0800179
Patrick Williams1318a5e2024-08-16 15:19:54 -0400180 bool sensorRez =
181 lbdUpdateSensorTree("/xyz/openbmc_project/sensors", sensorInterfaces);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800182
Scron Chang2703b022021-07-06 15:47:45 +0800183#ifdef FEATURE_HYBRID_SENSORS
184
185 if (!ipmi::sensor::sensors.empty())
186 {
187 for (const auto& sensor : ipmi::sensor::sensors)
188 {
189 // Threshold sensors should not be emplaced in here.
George Liuc024b392025-08-21 16:38:58 +0800190 if (sensor.second.sensorPath.starts_with(
191 "/xyz/openbmc_project/sensors/"))
Scron Chang2703b022021-07-06 15:47:45 +0800192 {
193 continue;
194 }
195
196 // The bus service name is not listed in ipmi::sensor::Info. Give it
197 // an empty string. For those function using non-threshold sensors,
198 // the bus service name will be retrieved in an alternative way.
199 boost::container::flat_map<std::string, std::vector<std::string>>
200 connectionMap{
201 {"", {sensor.second.propertyInterfaces.begin()->first}}};
202 sensorTreePtr->emplace(sensor.second.sensorPath, connectionMap);
203 }
204 }
205
206#endif
207
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800208 // Error if searching for sensors failed.
209 if (!sensorRez)
Willy Tude54f482021-01-26 15:59:09 -0800210 {
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800211 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800212 }
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800213
Johnathan Mantey04b0b072023-10-10 12:30:35 -0700214 filterSensors(*sensorTreePtr);
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800215 // Add VR control as optional search path.
216 (void)lbdUpdateSensorTree("/xyz/openbmc_project/vr", vrInterfaces);
217
Willy Tude54f482021-01-26 15:59:09 -0800218 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800219 sensorUpdatedIndex++;
Josh Lehana55c9532020-10-28 21:59:06 -0700220 // The SDR is being regenerated, wipe the old stats
221 sdrStatsTable.wipeTable();
Jie Yangf0a89942021-07-29 15:30:25 -0700222 sdrWriteTable.wipeTable();
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800223 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800224}
225
226bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
227{
228 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
229 bool sensorNumMapUpated = false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800230 static uint16_t prevSensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -0800231 std::shared_ptr<SensorSubTree> sensorTree;
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800232 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
Willy Tude54f482021-01-26 15:59:09 -0800233 if (!sensorTree)
234 {
235 return sensorNumMapUpated;
236 }
237
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800238 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
Willy Tude54f482021-01-26 15:59:09 -0800239 {
240 sensorNumMap = sensorNumMapPtr;
241 return sensorNumMapUpated;
242 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800243 prevSensorUpdatedIndex = curSensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -0800244
245 sensorNumMapPtr = std::make_shared<SensorNumMap>();
246
247 uint16_t sensorNum = 0;
248 uint16_t sensorIndex = 0;
249 for (const auto& sensor : *sensorTree)
250 {
251 sensorNumMapPtr->insert(
252 SensorNumMap::value_type(sensorNum, sensor.first));
253 sensorIndex++;
254 if (sensorIndex == maxSensorsPerLUN)
255 {
256 sensorIndex = lun1Sensor0;
257 }
258 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
259 {
260 // Skip assigning LUN 0x2 any sensors
261 sensorIndex = lun3Sensor0;
262 }
263 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
264 {
265 // this is an error, too many IPMI sensors
266 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
267 }
268 sensorNum = sensorIndex;
269 }
270 sensorNumMap = sensorNumMapPtr;
271 sensorNumMapUpated = true;
272 return sensorNumMapUpated;
273}
274} // namespace details
275
276bool getSensorSubtree(SensorSubTree& subtree)
277{
278 std::shared_ptr<SensorSubTree> sensorTree;
279 details::getSensorSubtree(sensorTree);
280 if (!sensorTree)
281 {
282 return false;
283 }
284
285 subtree = *sensorTree;
286 return true;
287}
288
Scron Chang2703b022021-07-06 15:47:45 +0800289#ifdef FEATURE_HYBRID_SENSORS
290// Static sensors are listed in sensor-gen.cpp.
Patrick Williams69b4c282025-03-03 11:19:13 -0500291ipmi::sensor::IdInfoMap::const_iterator findStaticSensor(
292 const std::string& path)
Scron Chang2703b022021-07-06 15:47:45 +0800293{
294 return std::find_if(
295 ipmi::sensor::sensors.begin(), ipmi::sensor::sensors.end(),
296 [&path](const ipmi::sensor::IdInfoMap::value_type& findSensor) {
Patrick Williams1318a5e2024-08-16 15:19:54 -0400297 return findSensor.second.sensorPath == path;
298 });
Scron Chang2703b022021-07-06 15:47:45 +0800299}
300#endif
301
Willy Tude54f482021-01-26 15:59:09 -0800302std::string getSensorTypeStringFromPath(const std::string& path)
303{
304 // get sensor type string from path, path is defined as
305 // /xyz/openbmc_project/sensors/<type>/label
306 size_t typeEnd = path.rfind("/");
307 if (typeEnd == std::string::npos)
308 {
309 return path;
310 }
311 size_t typeStart = path.rfind("/", typeEnd - 1);
312 if (typeStart == std::string::npos)
313 {
314 return path;
315 }
316 // Start at the character after the '/'
317 typeStart++;
318 return path.substr(typeStart, typeEnd - typeStart);
319}
320
321uint8_t getSensorTypeFromPath(const std::string& path)
322{
323 uint8_t sensorType = 0;
324 std::string type = getSensorTypeStringFromPath(path);
325 auto findSensor = sensorTypes.find(type.c_str());
326 if (findSensor != sensorTypes.end())
327 {
Scron Chang2b42d7e2021-07-06 15:45:47 +0800328 sensorType =
329 static_cast<uint8_t>(std::get<sensorTypeCodes>(findSensor->second));
Willy Tude54f482021-01-26 15:59:09 -0800330 } // else default 0x0 RESERVED
331
332 return sensorType;
333}
334
335uint16_t getSensorNumberFromPath(const std::string& path)
336{
337 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
338 details::getSensorNumMap(sensorNumMapPtr);
339 if (!sensorNumMapPtr)
340 {
341 return invalidSensorNumber;
342 }
343
344 try
345 {
346 return sensorNumMapPtr->right.at(path);
347 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500348 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800349 {
Willy Tude54f482021-01-26 15:59:09 -0800350 return invalidSensorNumber;
351 }
352}
353
354uint8_t getSensorEventTypeFromPath(const std::string& path)
355{
Scron Chang2b42d7e2021-07-06 15:45:47 +0800356 uint8_t sensorEventType = 0;
357 std::string type = getSensorTypeStringFromPath(path);
358 auto findSensor = sensorTypes.find(type.c_str());
359 if (findSensor != sensorTypes.end())
360 {
361 sensorEventType = static_cast<uint8_t>(
362 std::get<sensorEventTypeCodes>(findSensor->second));
363 }
364
365 return sensorEventType;
Willy Tude54f482021-01-26 15:59:09 -0800366}
367
368std::string getPathFromSensorNumber(uint16_t sensorNum)
369{
370 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
371 details::getSensorNumMap(sensorNumMapPtr);
372 if (!sensorNumMapPtr)
373 {
374 return std::string();
375 }
376
377 try
378 {
379 return sensorNumMapPtr->left.at(sensorNum);
380 }
Patrick Williamsa2ad2da2021-10-06 12:21:46 -0500381 catch (const std::out_of_range& e)
Willy Tude54f482021-01-26 15:59:09 -0800382 {
Willy Tude54f482021-01-26 15:59:09 -0800383 return std::string();
384 }
385}
386
387namespace ipmi
388{
389
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100390std::optional<std::map<std::string, std::vector<std::string>>>
Willy Tude54f482021-01-26 15:59:09 -0800391 getObjectInterfaces(const char* path)
392{
393 std::map<std::string, std::vector<std::string>> interfacesResponse;
394 std::vector<std::string> interfaces;
395 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
396
Alexander Hansen93aa9832025-11-07 15:29:49 +0100397 sdbusplus::message_t getObjectMessage = dbus->new_method_call(
398 ObjectMapper::default_service, ObjectMapper::instance_path,
399 ObjectMapper::interface, ObjectMapper::method_names::get_object);
Willy Tude54f482021-01-26 15:59:09 -0800400 getObjectMessage.append(path, interfaces);
401
402 try
403 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500404 sdbusplus::message_t response = dbus->call(getObjectMessage);
Willy Tude54f482021-01-26 15:59:09 -0800405 response.read(interfacesResponse);
406 }
407 catch (const std::exception& e)
408 {
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100409 return std::nullopt;
Willy Tude54f482021-01-26 15:59:09 -0800410 }
411
412 return interfacesResponse;
413}
414
Patrick Williams69b4c282025-03-03 11:19:13 -0500415std::map<std::string, Value> getEntityManagerProperties(const char* path,
416 const char* interface)
Willy Tude54f482021-01-26 15:59:09 -0800417{
418 std::map<std::string, Value> properties;
419 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
420
Patrick Williams5d82f472022-07-22 19:26:53 -0500421 sdbusplus::message_t getProperties =
Willy Tude54f482021-01-26 15:59:09 -0800422 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
423 "org.freedesktop.DBus.Properties", "GetAll");
424 getProperties.append(interface);
425
426 try
427 {
Patrick Williams5d82f472022-07-22 19:26:53 -0500428 sdbusplus::message_t response = dbus->call(getProperties);
Willy Tude54f482021-01-26 15:59:09 -0800429 response.read(properties);
430 }
431 catch (const std::exception& e)
432 {
George Liude6694e2024-07-17 15:22:25 +0800433 lg2::error("Failed to GetAll, path: {PATH}, interface: {INTERFACE}, "
434 "error: {ERROR}",
435 "PATH", path, "INTERFACE", interface, "ERROR", e);
Willy Tude54f482021-01-26 15:59:09 -0800436 }
437
438 return properties;
439}
440
Willy Tu4eca2512022-06-20 21:14:51 -0700441// Fetch the ipmiDecoratorPaths to get the list of dbus objects that
442// have ipmi decorator to prevent unnessary dbus call to fetch the info
Patrick Williams69b4c282025-03-03 11:19:13 -0500443std::optional<std::unordered_set<std::string>>& getIpmiDecoratorPaths(
444 const std::optional<ipmi::Context::ptr>& ctx)
Willy Tu4eca2512022-06-20 21:14:51 -0700445{
446 static std::optional<std::unordered_set<std::string>> ipmiDecoratorPaths;
447
448 if (!ctx.has_value() || ipmiDecoratorPaths != std::nullopt)
449 {
450 return ipmiDecoratorPaths;
451 }
452
George Liude1420d2025-03-03 15:14:25 +0800453 using Paths = std::vector<std::string>;
Willy Tu4eca2512022-06-20 21:14:51 -0700454 boost::system::error_code ec;
George Liude1420d2025-03-03 15:14:25 +0800455 Paths paths = ipmi::callDbusMethod<Paths>(
Alexander Hansen93aa9832025-11-07 15:29:49 +0100456 *ctx, ec, ObjectMapper::default_service, ObjectMapper::instance_path,
457 ObjectMapper::interface, ObjectMapper::method_names::get_sub_tree_paths,
458 "/", int32_t(0),
George Liude1420d2025-03-03 15:14:25 +0800459 std::array<const char*, 1>{
460 "xyz.openbmc_project.Inventory.Decorator.Ipmi"});
461
Willy Tu4eca2512022-06-20 21:14:51 -0700462 if (ec)
463 {
464 return ipmiDecoratorPaths;
465 }
466
Patrick Williams1318a5e2024-08-16 15:19:54 -0400467 ipmiDecoratorPaths =
468 std::unordered_set<std::string>(paths.begin(), paths.end());
Willy Tu4eca2512022-06-20 21:14:51 -0700469 return ipmiDecoratorPaths;
470}
471
Willy Tude54f482021-01-26 15:59:09 -0800472const std::string* getSensorConfigurationInterface(
473 const std::map<std::string, std::vector<std::string>>&
474 sensorInterfacesResponse)
475{
476 auto entityManagerService =
477 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
478 if (entityManagerService == sensorInterfacesResponse.end())
479 {
480 return nullptr;
481 }
482
483 // Find the fan configuration first (fans can have multiple configuration
484 // interfaces).
485 for (const auto& entry : entityManagerService->second)
486 {
487 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
488 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
489 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
490 {
491 return &entry;
492 }
493 }
494
495 for (const auto& entry : entityManagerService->second)
496 {
George Liuc024b392025-08-21 16:38:58 +0800497 if (entry.starts_with("xyz.openbmc_project.Configuration."))
Willy Tude54f482021-01-26 15:59:09 -0800498 {
499 return &entry;
500 }
501 }
502
503 return nullptr;
504}
505
506// Follow Association properties for Sensor back to the Board dbus object to
507// check for an EntityId and EntityInstance property.
Willy Tu4eca2512022-06-20 21:14:51 -0700508void updateIpmiFromAssociation(
509 const std::string& path,
510 const std::unordered_set<std::string>& ipmiDecoratorPaths,
511 const DbusInterfaceMap& sensorMap, uint8_t& entityId,
512 uint8_t& entityInstance)
Willy Tude54f482021-01-26 15:59:09 -0800513{
514 namespace fs = std::filesystem;
515
516 auto sensorAssociationObject =
517 sensorMap.find("xyz.openbmc_project.Association.Definitions");
518 if (sensorAssociationObject == sensorMap.end())
519 {
520 if constexpr (debug)
521 {
522 std::fprintf(stderr, "path=%s, no association interface found\n",
523 path.c_str());
524 }
525
526 return;
527 }
528
529 auto associationObject =
530 sensorAssociationObject->second.find("Associations");
531 if (associationObject == sensorAssociationObject->second.end())
532 {
533 if constexpr (debug)
534 {
535 std::fprintf(stderr, "path=%s, no association records found\n",
536 path.c_str());
537 }
538
539 return;
540 }
541
542 std::vector<Association> associationValues =
543 std::get<std::vector<Association>>(associationObject->second);
544
545 // loop through the Associations looking for the right one:
546 for (const auto& entry : associationValues)
547 {
548 // forward, reverse, endpoint
549 const std::string& forward = std::get<0>(entry);
550 const std::string& reverse = std::get<1>(entry);
551 const std::string& endpoint = std::get<2>(entry);
552
553 // We only currently concern ourselves with chassis+all_sensors.
554 if (!(forward == "chassis" && reverse == "all_sensors"))
555 {
556 continue;
557 }
558
559 // the endpoint is the board entry provided by
560 // Entity-Manager. so let's grab its properties if it has
561 // the right interface.
562
563 // just try grabbing the properties first.
Willy Tu4eca2512022-06-20 21:14:51 -0700564 ipmi::PropertyMap::iterator entityIdProp;
565 ipmi::PropertyMap::iterator entityInstanceProp;
566 if (ipmiDecoratorPaths.contains(endpoint))
567 {
568 std::map<std::string, Value> ipmiProperties =
569 getEntityManagerProperties(
570 endpoint.c_str(),
571 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
Willy Tude54f482021-01-26 15:59:09 -0800572
Willy Tu4eca2512022-06-20 21:14:51 -0700573 entityIdProp = ipmiProperties.find("EntityId");
574 entityInstanceProp = ipmiProperties.find("EntityInstance");
575 if (entityIdProp != ipmiProperties.end())
576 {
577 entityId = static_cast<uint8_t>(
578 std::get<uint64_t>(entityIdProp->second));
579 }
580 if (entityInstanceProp != ipmiProperties.end())
581 {
582 entityInstance = static_cast<uint8_t>(
583 std::get<uint64_t>(entityInstanceProp->second));
584 }
Willy Tude54f482021-01-26 15:59:09 -0800585 }
586
587 // Now check the entity-manager entry for this sensor to see
588 // if it has its own value and use that instead.
589 //
590 // In theory, checking this first saves us from checking
591 // both, except in most use-cases identified, there won't be
592 // a per sensor override, so we need to always check both.
593 std::string sensorNameFromPath = fs::path(path).filename();
594
595 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
596
597 // Download the interfaces for the sensor from
598 // Entity-Manager to find the name of the configuration
599 // interface.
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100600 std::optional<std::map<std::string, std::vector<std::string>>>
601 sensorInterfacesResponseOpt =
Willy Tude54f482021-01-26 15:59:09 -0800602 getObjectInterfaces(sensorConfigPath.c_str());
603
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100604 if (!sensorInterfacesResponseOpt.has_value())
605 {
George Liude6694e2024-07-17 15:22:25 +0800606 lg2::debug("Failed to GetObject, path: {PATH}", "PATH",
607 sensorConfigPath);
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100608 continue;
609 }
610
Willy Tude54f482021-01-26 15:59:09 -0800611 const std::string* configurationInterface =
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100612 getSensorConfigurationInterface(
613 sensorInterfacesResponseOpt.value());
Willy Tude54f482021-01-26 15:59:09 -0800614
Harvey Wu7bb84db2023-06-26 10:02:08 +0800615 // If there are multi association path settings and only one path exist,
616 // we need to continue if cannot find configuration interface for this
617 // sensor.
Willy Tude54f482021-01-26 15:59:09 -0800618 if (!configurationInterface)
619 {
Harvey Wu7bb84db2023-06-26 10:02:08 +0800620 continue;
Willy Tude54f482021-01-26 15:59:09 -0800621 }
622
623 // We found a configuration interface.
624 std::map<std::string, Value> configurationProperties =
625 getEntityManagerProperties(sensorConfigPath.c_str(),
626 configurationInterface->c_str());
627
628 entityIdProp = configurationProperties.find("EntityId");
629 entityInstanceProp = configurationProperties.find("EntityInstance");
630 if (entityIdProp != configurationProperties.end())
631 {
632 entityId =
633 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
634 }
635 if (entityInstanceProp != configurationProperties.end())
636 {
637 entityInstance = static_cast<uint8_t>(
638 std::get<uint64_t>(entityInstanceProp->second));
639 }
640
641 // stop searching Association records.
642 break;
643 } // end for Association vectors.
644
645 if constexpr (debug)
646 {
647 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
648 path.c_str(), entityId, entityInstance);
649 }
650}
651
652} // namespace ipmi