| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 1 | /* | 
|  | 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 Liu | c1c7eac | 2024-02-04 17:24:19 +0800 | [diff] [blame] | 19 | #include <ipmid/utils.hpp> | 
| Johnathan Mantey | 04b0b07 | 2023-10-10 12:30:35 -0700 | [diff] [blame] | 20 | #include <nlohmann/json.hpp> | 
| George Liu | de6694e | 2024-07-17 15:22:25 +0800 | [diff] [blame] | 21 | #include <phosphor-logging/lg2.hpp> | 
| Johnathan Mantey | 04b0b07 | 2023-10-10 12:30:35 -0700 | [diff] [blame] | 22 |  | 
|  | 23 | #include <fstream> | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 24 | #include <optional> | 
|  | 25 | #include <unordered_set> | 
|  | 26 |  | 
| Scron Chang | 2703b02 | 2021-07-06 15:47:45 +0800 | [diff] [blame] | 27 | #ifdef FEATURE_HYBRID_SENSORS | 
|  | 28 |  | 
|  | 29 | #include <ipmid/utils.hpp> | 
|  | 30 | namespace ipmi | 
|  | 31 | { | 
|  | 32 | namespace sensor | 
|  | 33 | { | 
|  | 34 | extern const IdInfoMap sensors; | 
|  | 35 | } // namespace sensor | 
|  | 36 | } // namespace ipmi | 
|  | 37 |  | 
|  | 38 | #endif | 
|  | 39 |  | 
| Johnathan Mantey | 34d1973 | 2024-07-17 07:59:54 -0700 | [diff] [blame] | 40 | boost::container::flat_map< | 
|  | 41 | const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr> | 
|  | 42 | sensorTypes{ | 
|  | 43 | {{"temperature", std::make_pair(SensorTypeCodes::temperature, | 
|  | 44 | SensorEventTypeCodes::threshold)}, | 
|  | 45 | {"voltage", std::make_pair(SensorTypeCodes::voltage, | 
|  | 46 | SensorEventTypeCodes::threshold)}, | 
|  | 47 | {"current", std::make_pair(SensorTypeCodes::current, | 
|  | 48 | SensorEventTypeCodes::threshold)}, | 
|  | 49 | {"fan_tach", std::make_pair(SensorTypeCodes::fan, | 
|  | 50 | SensorEventTypeCodes::threshold)}, | 
|  | 51 | {"fan_pwm", std::make_pair(SensorTypeCodes::fan, | 
|  | 52 | SensorEventTypeCodes::threshold)}, | 
| George Liu | f2807ed | 2025-04-03 15:52:57 +0800 | [diff] [blame] | 53 | {"intrusion", std::make_pair(SensorTypeCodes::physicalSecurity, | 
| Johnathan Mantey | 34d1973 | 2024-07-17 07:59:54 -0700 | [diff] [blame] | 54 | SensorEventTypeCodes::sensorSpecified)}, | 
|  | 55 | {"processor", std::make_pair(SensorTypeCodes::processor, | 
|  | 56 | SensorEventTypeCodes::sensorSpecified)}, | 
|  | 57 | {"power", std::make_pair(SensorTypeCodes::other, | 
|  | 58 | SensorEventTypeCodes::threshold)}, | 
|  | 59 | {"memory", std::make_pair(SensorTypeCodes::memory, | 
|  | 60 | SensorEventTypeCodes::sensorSpecified)}, | 
| George Liu | f2807ed | 2025-04-03 15:52:57 +0800 | [diff] [blame] | 61 | {"state", std::make_pair(SensorTypeCodes::powerUnit, | 
| Johnathan Mantey | 34d1973 | 2024-07-17 07:59:54 -0700 | [diff] [blame] | 62 | SensorEventTypeCodes::sensorSpecified)}, | 
|  | 63 | {"buttons", std::make_pair(SensorTypeCodes::buttons, | 
|  | 64 | SensorEventTypeCodes::sensorSpecified)}, | 
|  | 65 | {"watchdog", std::make_pair(SensorTypeCodes::watchdog2, | 
|  | 66 | SensorEventTypeCodes::sensorSpecified)}, | 
|  | 67 | {"entity", std::make_pair(SensorTypeCodes::entity, | 
|  | 68 | SensorEventTypeCodes::sensorSpecified)}, | 
|  | 69 | {"energy", std::make_pair(SensorTypeCodes::other, | 
|  | 70 | SensorEventTypeCodes::threshold)}}}; | 
|  | 71 |  | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 72 | namespace details | 
|  | 73 | { | 
| Johnathan Mantey | 04b0b07 | 2023-10-10 12:30:35 -0700 | [diff] [blame] | 74 |  | 
|  | 75 | // IPMI supports a smaller number of sensors than are available via Redfish. | 
|  | 76 | // Trim the list of sensors, via a configuration file. | 
|  | 77 | // Read the IPMI Sensor Filtering section in docs/configuration.md for | 
|  | 78 | // a more detailed description. | 
|  | 79 | static void filterSensors(SensorSubTree& subtree) | 
|  | 80 | { | 
|  | 81 | constexpr const char* filterFilename = | 
|  | 82 | "/usr/share/ipmi-providers/sensor_filter.json"; | 
|  | 83 | std::ifstream filterFile(filterFilename); | 
|  | 84 | if (!filterFile.good()) | 
|  | 85 | { | 
|  | 86 | return; | 
|  | 87 | } | 
| Patrick Williams | 1318a5e | 2024-08-16 15:19:54 -0400 | [diff] [blame] | 88 | nlohmann::json sensorFilterJSON = | 
|  | 89 | nlohmann::json::parse(filterFile, nullptr, false); | 
| Johnathan Mantey | 04b0b07 | 2023-10-10 12:30:35 -0700 | [diff] [blame] | 90 | nlohmann::json::iterator svcFilterit = | 
|  | 91 | sensorFilterJSON.find("ServiceFilter"); | 
|  | 92 | if (svcFilterit == sensorFilterJSON.end()) | 
|  | 93 | { | 
|  | 94 | return; | 
|  | 95 | } | 
|  | 96 |  | 
|  | 97 | subtree.erase(std::remove_if(subtree.begin(), subtree.end(), | 
|  | 98 | [svcFilterit](SensorSubTree::value_type& kv) { | 
| Patrick Williams | 1318a5e | 2024-08-16 15:19:54 -0400 | [diff] [blame] | 99 | auto& [_, serviceToIfaces] = kv; | 
| Johnathan Mantey | 04b0b07 | 2023-10-10 12:30:35 -0700 | [diff] [blame] | 100 |  | 
| Patrick Williams | 1318a5e | 2024-08-16 15:19:54 -0400 | [diff] [blame] | 101 | for (auto service = svcFilterit->begin(); | 
|  | 102 | service != svcFilterit->end(); | 
|  | 103 | ++service) | 
|  | 104 | { | 
|  | 105 | serviceToIfaces.erase(*service); | 
|  | 106 | } | 
|  | 107 | return serviceToIfaces.empty(); | 
|  | 108 | }), | 
| Johnathan Mantey | 04b0b07 | 2023-10-10 12:30:35 -0700 | [diff] [blame] | 109 | subtree.end()); | 
|  | 110 | } | 
|  | 111 |  | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 112 | uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree) | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 113 | { | 
|  | 114 | static std::shared_ptr<SensorSubTree> sensorTreePtr; | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 115 | static uint16_t sensorUpdatedIndex = 0; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 116 | std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); | 
| Patrick Williams | 5d82f47 | 2022-07-22 19:26:53 -0500 | [diff] [blame] | 117 | static sdbusplus::bus::match_t sensorAdded( | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 118 | *dbus, | 
|  | 119 | "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/" | 
|  | 120 | "sensors/'", | 
| Patrick Williams | 5d82f47 | 2022-07-22 19:26:53 -0500 | [diff] [blame] | 121 | [](sdbusplus::message_t&) { sensorTreePtr.reset(); }); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 122 |  | 
| Patrick Williams | 5d82f47 | 2022-07-22 19:26:53 -0500 | [diff] [blame] | 123 | static sdbusplus::bus::match_t sensorRemoved( | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 124 | *dbus, | 
|  | 125 | "type='signal',member='InterfacesRemoved',arg0path='/xyz/" | 
|  | 126 | "openbmc_project/sensors/'", | 
| Patrick Williams | 5d82f47 | 2022-07-22 19:26:53 -0500 | [diff] [blame] | 127 | [](sdbusplus::message_t&) { sensorTreePtr.reset(); }); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 128 |  | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 129 | if (sensorTreePtr) | 
|  | 130 | { | 
|  | 131 | subtree = sensorTreePtr; | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 132 | return sensorUpdatedIndex; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 133 | } | 
|  | 134 |  | 
|  | 135 | sensorTreePtr = std::make_shared<SensorSubTree>(); | 
|  | 136 |  | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 137 | static constexpr const int32_t depth = 2; | 
| Hao Jiang | 9a5b51e | 2021-01-06 10:32:22 -0800 | [diff] [blame] | 138 |  | 
| Patrick Williams | 369824e | 2023-10-20 11:18:23 -0500 | [diff] [blame] | 139 | auto lbdUpdateSensorTree = [&dbus](const char* path, | 
|  | 140 | const auto& interfaces) { | 
| Hao Jiang | 9a5b51e | 2021-01-06 10:32:22 -0800 | [diff] [blame] | 141 | auto mapperCall = dbus->new_method_call( | 
|  | 142 | "xyz.openbmc_project.ObjectMapper", | 
|  | 143 | "/xyz/openbmc_project/object_mapper", | 
|  | 144 | "xyz.openbmc_project.ObjectMapper", "GetSubTree"); | 
|  | 145 | SensorSubTree sensorTreePartial; | 
|  | 146 |  | 
|  | 147 | mapperCall.append(path, depth, interfaces); | 
|  | 148 |  | 
|  | 149 | try | 
|  | 150 | { | 
|  | 151 | auto mapperReply = dbus->call(mapperCall); | 
|  | 152 | mapperReply.read(sensorTreePartial); | 
|  | 153 | } | 
| Patrick Williams | a2ad2da | 2021-10-06 12:21:46 -0500 | [diff] [blame] | 154 | catch (const sdbusplus::exception_t& e) | 
| Hao Jiang | 9a5b51e | 2021-01-06 10:32:22 -0800 | [diff] [blame] | 155 | { | 
| George Liu | de6694e | 2024-07-17 15:22:25 +0800 | [diff] [blame] | 156 | lg2::error("Failed to update subtree, path: {PATH}, error: {ERROR}", | 
|  | 157 | "PATH", path, "ERROR", e); | 
| Hao Jiang | 9a5b51e | 2021-01-06 10:32:22 -0800 | [diff] [blame] | 158 | return false; | 
|  | 159 | } | 
|  | 160 | if constexpr (debug) | 
|  | 161 | { | 
|  | 162 | std::fprintf(stderr, "IPMI updated: %zu sensors under %s\n", | 
|  | 163 | sensorTreePartial.size(), path); | 
|  | 164 | } | 
|  | 165 | sensorTreePtr->merge(std::move(sensorTreePartial)); | 
|  | 166 | return true; | 
|  | 167 | }; | 
|  | 168 |  | 
|  | 169 | // Add sensors to SensorTree | 
|  | 170 | static constexpr const std::array sensorInterfaces = { | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 171 | "xyz.openbmc_project.Sensor.Value", | 
| Jie Yang | f0a8994 | 2021-07-29 15:30:25 -0700 | [diff] [blame] | 172 | "xyz.openbmc_project.Sensor.ValueMutability", | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 173 | "xyz.openbmc_project.Sensor.Threshold.Warning", | 
|  | 174 | "xyz.openbmc_project.Sensor.Threshold.Critical"}; | 
| Hao Jiang | 9a5b51e | 2021-01-06 10:32:22 -0800 | [diff] [blame] | 175 | static constexpr const std::array vrInterfaces = { | 
|  | 176 | "xyz.openbmc_project.Control.VoltageRegulatorMode"}; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 177 |  | 
| Patrick Williams | 1318a5e | 2024-08-16 15:19:54 -0400 | [diff] [blame] | 178 | bool sensorRez = | 
|  | 179 | lbdUpdateSensorTree("/xyz/openbmc_project/sensors", sensorInterfaces); | 
| Hao Jiang | 9a5b51e | 2021-01-06 10:32:22 -0800 | [diff] [blame] | 180 |  | 
| Scron Chang | 2703b02 | 2021-07-06 15:47:45 +0800 | [diff] [blame] | 181 | #ifdef FEATURE_HYBRID_SENSORS | 
|  | 182 |  | 
|  | 183 | if (!ipmi::sensor::sensors.empty()) | 
|  | 184 | { | 
|  | 185 | for (const auto& sensor : ipmi::sensor::sensors) | 
|  | 186 | { | 
|  | 187 | // Threshold sensors should not be emplaced in here. | 
|  | 188 | if (boost::starts_with(sensor.second.sensorPath, | 
|  | 189 | "/xyz/openbmc_project/sensors/")) | 
|  | 190 | { | 
|  | 191 | continue; | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | // The bus service name is not listed in ipmi::sensor::Info. Give it | 
|  | 195 | // an empty string. For those function using non-threshold sensors, | 
|  | 196 | // the bus service name will be retrieved in an alternative way. | 
|  | 197 | boost::container::flat_map<std::string, std::vector<std::string>> | 
|  | 198 | connectionMap{ | 
|  | 199 | {"", {sensor.second.propertyInterfaces.begin()->first}}}; | 
|  | 200 | sensorTreePtr->emplace(sensor.second.sensorPath, connectionMap); | 
|  | 201 | } | 
|  | 202 | } | 
|  | 203 |  | 
|  | 204 | #endif | 
|  | 205 |  | 
| Hao Jiang | 9a5b51e | 2021-01-06 10:32:22 -0800 | [diff] [blame] | 206 | // Error if searching for sensors failed. | 
|  | 207 | if (!sensorRez) | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 208 | { | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 209 | return sensorUpdatedIndex; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 210 | } | 
| Hao Jiang | 9a5b51e | 2021-01-06 10:32:22 -0800 | [diff] [blame] | 211 |  | 
| Johnathan Mantey | 04b0b07 | 2023-10-10 12:30:35 -0700 | [diff] [blame] | 212 | filterSensors(*sensorTreePtr); | 
| Hao Jiang | 9a5b51e | 2021-01-06 10:32:22 -0800 | [diff] [blame] | 213 | // Add VR control as optional search path. | 
|  | 214 | (void)lbdUpdateSensorTree("/xyz/openbmc_project/vr", vrInterfaces); | 
|  | 215 |  | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 216 | subtree = sensorTreePtr; | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 217 | sensorUpdatedIndex++; | 
| Josh Lehan | a55c953 | 2020-10-28 21:59:06 -0700 | [diff] [blame] | 218 | // The SDR is being regenerated, wipe the old stats | 
|  | 219 | sdrStatsTable.wipeTable(); | 
| Jie Yang | f0a8994 | 2021-07-29 15:30:25 -0700 | [diff] [blame] | 220 | sdrWriteTable.wipeTable(); | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 221 | return sensorUpdatedIndex; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 222 | } | 
|  | 223 |  | 
|  | 224 | bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap) | 
|  | 225 | { | 
|  | 226 | static std::shared_ptr<SensorNumMap> sensorNumMapPtr; | 
|  | 227 | bool sensorNumMapUpated = false; | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 228 | static uint16_t prevSensorUpdatedIndex = 0; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 229 | std::shared_ptr<SensorSubTree> sensorTree; | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 230 | uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 231 | if (!sensorTree) | 
|  | 232 | { | 
|  | 233 | return sensorNumMapUpated; | 
|  | 234 | } | 
|  | 235 |  | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 236 | if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr) | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 237 | { | 
|  | 238 | sensorNumMap = sensorNumMapPtr; | 
|  | 239 | return sensorNumMapUpated; | 
|  | 240 | } | 
| Kuiying Wang | a8b5b26 | 2021-02-06 23:38:22 +0800 | [diff] [blame] | 241 | prevSensorUpdatedIndex = curSensorUpdatedIndex; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 242 |  | 
|  | 243 | sensorNumMapPtr = std::make_shared<SensorNumMap>(); | 
|  | 244 |  | 
|  | 245 | uint16_t sensorNum = 0; | 
|  | 246 | uint16_t sensorIndex = 0; | 
|  | 247 | for (const auto& sensor : *sensorTree) | 
|  | 248 | { | 
|  | 249 | sensorNumMapPtr->insert( | 
|  | 250 | SensorNumMap::value_type(sensorNum, sensor.first)); | 
|  | 251 | sensorIndex++; | 
|  | 252 | if (sensorIndex == maxSensorsPerLUN) | 
|  | 253 | { | 
|  | 254 | sensorIndex = lun1Sensor0; | 
|  | 255 | } | 
|  | 256 | else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN)) | 
|  | 257 | { | 
|  | 258 | // Skip assigning LUN 0x2 any sensors | 
|  | 259 | sensorIndex = lun3Sensor0; | 
|  | 260 | } | 
|  | 261 | else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN)) | 
|  | 262 | { | 
|  | 263 | // this is an error, too many IPMI sensors | 
|  | 264 | throw std::out_of_range("Maximum number of IPMI sensors exceeded."); | 
|  | 265 | } | 
|  | 266 | sensorNum = sensorIndex; | 
|  | 267 | } | 
|  | 268 | sensorNumMap = sensorNumMapPtr; | 
|  | 269 | sensorNumMapUpated = true; | 
|  | 270 | return sensorNumMapUpated; | 
|  | 271 | } | 
|  | 272 | } // namespace details | 
|  | 273 |  | 
|  | 274 | bool getSensorSubtree(SensorSubTree& subtree) | 
|  | 275 | { | 
|  | 276 | std::shared_ptr<SensorSubTree> sensorTree; | 
|  | 277 | details::getSensorSubtree(sensorTree); | 
|  | 278 | if (!sensorTree) | 
|  | 279 | { | 
|  | 280 | return false; | 
|  | 281 | } | 
|  | 282 |  | 
|  | 283 | subtree = *sensorTree; | 
|  | 284 | return true; | 
|  | 285 | } | 
|  | 286 |  | 
| Scron Chang | 2703b02 | 2021-07-06 15:47:45 +0800 | [diff] [blame] | 287 | #ifdef FEATURE_HYBRID_SENSORS | 
|  | 288 | // Static sensors are listed in sensor-gen.cpp. | 
| Patrick Williams | 69b4c28 | 2025-03-03 11:19:13 -0500 | [diff] [blame] | 289 | ipmi::sensor::IdInfoMap::const_iterator findStaticSensor( | 
|  | 290 | const std::string& path) | 
| Scron Chang | 2703b02 | 2021-07-06 15:47:45 +0800 | [diff] [blame] | 291 | { | 
|  | 292 | return std::find_if( | 
|  | 293 | ipmi::sensor::sensors.begin(), ipmi::sensor::sensors.end(), | 
|  | 294 | [&path](const ipmi::sensor::IdInfoMap::value_type& findSensor) { | 
| Patrick Williams | 1318a5e | 2024-08-16 15:19:54 -0400 | [diff] [blame] | 295 | return findSensor.second.sensorPath == path; | 
|  | 296 | }); | 
| Scron Chang | 2703b02 | 2021-07-06 15:47:45 +0800 | [diff] [blame] | 297 | } | 
|  | 298 | #endif | 
|  | 299 |  | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 300 | std::string getSensorTypeStringFromPath(const std::string& path) | 
|  | 301 | { | 
|  | 302 | // get sensor type string from path, path is defined as | 
|  | 303 | // /xyz/openbmc_project/sensors/<type>/label | 
|  | 304 | size_t typeEnd = path.rfind("/"); | 
|  | 305 | if (typeEnd == std::string::npos) | 
|  | 306 | { | 
|  | 307 | return path; | 
|  | 308 | } | 
|  | 309 | size_t typeStart = path.rfind("/", typeEnd - 1); | 
|  | 310 | if (typeStart == std::string::npos) | 
|  | 311 | { | 
|  | 312 | return path; | 
|  | 313 | } | 
|  | 314 | // Start at the character after the '/' | 
|  | 315 | typeStart++; | 
|  | 316 | return path.substr(typeStart, typeEnd - typeStart); | 
|  | 317 | } | 
|  | 318 |  | 
|  | 319 | uint8_t getSensorTypeFromPath(const std::string& path) | 
|  | 320 | { | 
|  | 321 | uint8_t sensorType = 0; | 
|  | 322 | std::string type = getSensorTypeStringFromPath(path); | 
|  | 323 | auto findSensor = sensorTypes.find(type.c_str()); | 
|  | 324 | if (findSensor != sensorTypes.end()) | 
|  | 325 | { | 
| Scron Chang | 2b42d7e | 2021-07-06 15:45:47 +0800 | [diff] [blame] | 326 | sensorType = | 
|  | 327 | static_cast<uint8_t>(std::get<sensorTypeCodes>(findSensor->second)); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 328 | } // else default 0x0 RESERVED | 
|  | 329 |  | 
|  | 330 | return sensorType; | 
|  | 331 | } | 
|  | 332 |  | 
|  | 333 | uint16_t getSensorNumberFromPath(const std::string& path) | 
|  | 334 | { | 
|  | 335 | std::shared_ptr<SensorNumMap> sensorNumMapPtr; | 
|  | 336 | details::getSensorNumMap(sensorNumMapPtr); | 
|  | 337 | if (!sensorNumMapPtr) | 
|  | 338 | { | 
|  | 339 | return invalidSensorNumber; | 
|  | 340 | } | 
|  | 341 |  | 
|  | 342 | try | 
|  | 343 | { | 
|  | 344 | return sensorNumMapPtr->right.at(path); | 
|  | 345 | } | 
| Patrick Williams | a2ad2da | 2021-10-06 12:21:46 -0500 | [diff] [blame] | 346 | catch (const std::out_of_range& e) | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 347 | { | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 348 | return invalidSensorNumber; | 
|  | 349 | } | 
|  | 350 | } | 
|  | 351 |  | 
|  | 352 | uint8_t getSensorEventTypeFromPath(const std::string& path) | 
|  | 353 | { | 
| Scron Chang | 2b42d7e | 2021-07-06 15:45:47 +0800 | [diff] [blame] | 354 | uint8_t sensorEventType = 0; | 
|  | 355 | std::string type = getSensorTypeStringFromPath(path); | 
|  | 356 | auto findSensor = sensorTypes.find(type.c_str()); | 
|  | 357 | if (findSensor != sensorTypes.end()) | 
|  | 358 | { | 
|  | 359 | sensorEventType = static_cast<uint8_t>( | 
|  | 360 | std::get<sensorEventTypeCodes>(findSensor->second)); | 
|  | 361 | } | 
|  | 362 |  | 
|  | 363 | return sensorEventType; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 364 | } | 
|  | 365 |  | 
|  | 366 | std::string getPathFromSensorNumber(uint16_t sensorNum) | 
|  | 367 | { | 
|  | 368 | std::shared_ptr<SensorNumMap> sensorNumMapPtr; | 
|  | 369 | details::getSensorNumMap(sensorNumMapPtr); | 
|  | 370 | if (!sensorNumMapPtr) | 
|  | 371 | { | 
|  | 372 | return std::string(); | 
|  | 373 | } | 
|  | 374 |  | 
|  | 375 | try | 
|  | 376 | { | 
|  | 377 | return sensorNumMapPtr->left.at(sensorNum); | 
|  | 378 | } | 
| Patrick Williams | a2ad2da | 2021-10-06 12:21:46 -0500 | [diff] [blame] | 379 | catch (const std::out_of_range& e) | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 380 | { | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 381 | return std::string(); | 
|  | 382 | } | 
|  | 383 | } | 
|  | 384 |  | 
|  | 385 | namespace ipmi | 
|  | 386 | { | 
|  | 387 |  | 
| Alexander Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 388 | std::optional<std::map<std::string, std::vector<std::string>>> | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 389 | getObjectInterfaces(const char* path) | 
|  | 390 | { | 
|  | 391 | std::map<std::string, std::vector<std::string>> interfacesResponse; | 
|  | 392 | std::vector<std::string> interfaces; | 
|  | 393 | std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); | 
|  | 394 |  | 
| Patrick Williams | 5d82f47 | 2022-07-22 19:26:53 -0500 | [diff] [blame] | 395 | sdbusplus::message_t getObjectMessage = | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 396 | dbus->new_method_call("xyz.openbmc_project.ObjectMapper", | 
|  | 397 | "/xyz/openbmc_project/object_mapper", | 
|  | 398 | "xyz.openbmc_project.ObjectMapper", "GetObject"); | 
|  | 399 | getObjectMessage.append(path, interfaces); | 
|  | 400 |  | 
|  | 401 | try | 
|  | 402 | { | 
| Patrick Williams | 5d82f47 | 2022-07-22 19:26:53 -0500 | [diff] [blame] | 403 | sdbusplus::message_t response = dbus->call(getObjectMessage); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 404 | response.read(interfacesResponse); | 
|  | 405 | } | 
|  | 406 | catch (const std::exception& e) | 
|  | 407 | { | 
| Alexander Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 408 | return std::nullopt; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 409 | } | 
|  | 410 |  | 
|  | 411 | return interfacesResponse; | 
|  | 412 | } | 
|  | 413 |  | 
| Patrick Williams | 69b4c28 | 2025-03-03 11:19:13 -0500 | [diff] [blame] | 414 | std::map<std::string, Value> getEntityManagerProperties(const char* path, | 
|  | 415 | const char* interface) | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 416 | { | 
|  | 417 | std::map<std::string, Value> properties; | 
|  | 418 | std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus(); | 
|  | 419 |  | 
| Patrick Williams | 5d82f47 | 2022-07-22 19:26:53 -0500 | [diff] [blame] | 420 | sdbusplus::message_t getProperties = | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 421 | dbus->new_method_call("xyz.openbmc_project.EntityManager", path, | 
|  | 422 | "org.freedesktop.DBus.Properties", "GetAll"); | 
|  | 423 | getProperties.append(interface); | 
|  | 424 |  | 
|  | 425 | try | 
|  | 426 | { | 
| Patrick Williams | 5d82f47 | 2022-07-22 19:26:53 -0500 | [diff] [blame] | 427 | sdbusplus::message_t response = dbus->call(getProperties); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 428 | response.read(properties); | 
|  | 429 | } | 
|  | 430 | catch (const std::exception& e) | 
|  | 431 | { | 
| George Liu | de6694e | 2024-07-17 15:22:25 +0800 | [diff] [blame] | 432 | lg2::error("Failed to GetAll, path: {PATH}, interface: {INTERFACE}, " | 
|  | 433 | "error: {ERROR}", | 
|  | 434 | "PATH", path, "INTERFACE", interface, "ERROR", e); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 435 | } | 
|  | 436 |  | 
|  | 437 | return properties; | 
|  | 438 | } | 
|  | 439 |  | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 440 | // Fetch the ipmiDecoratorPaths to get the list of dbus objects that | 
|  | 441 | // have ipmi decorator to prevent unnessary dbus call to fetch the info | 
| Patrick Williams | 69b4c28 | 2025-03-03 11:19:13 -0500 | [diff] [blame] | 442 | std::optional<std::unordered_set<std::string>>& getIpmiDecoratorPaths( | 
|  | 443 | const std::optional<ipmi::Context::ptr>& ctx) | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 444 | { | 
|  | 445 | static std::optional<std::unordered_set<std::string>> ipmiDecoratorPaths; | 
|  | 446 |  | 
|  | 447 | if (!ctx.has_value() || ipmiDecoratorPaths != std::nullopt) | 
|  | 448 | { | 
|  | 449 | return ipmiDecoratorPaths; | 
|  | 450 | } | 
|  | 451 |  | 
| George Liu | de1420d | 2025-03-03 15:14:25 +0800 | [diff] [blame] | 452 | using Paths = std::vector<std::string>; | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 453 | boost::system::error_code ec; | 
| George Liu | de1420d | 2025-03-03 15:14:25 +0800 | [diff] [blame] | 454 | Paths paths = ipmi::callDbusMethod<Paths>( | 
|  | 455 | *ctx, ec, "xyz.openbmc_project.ObjectMapper", | 
|  | 456 | "/xyz/openbmc_project/object_mapper", | 
|  | 457 | "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", int32_t(0), | 
|  | 458 | std::array<const char*, 1>{ | 
|  | 459 | "xyz.openbmc_project.Inventory.Decorator.Ipmi"}); | 
|  | 460 |  | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 461 | if (ec) | 
|  | 462 | { | 
|  | 463 | return ipmiDecoratorPaths; | 
|  | 464 | } | 
|  | 465 |  | 
| Patrick Williams | 1318a5e | 2024-08-16 15:19:54 -0400 | [diff] [blame] | 466 | ipmiDecoratorPaths = | 
|  | 467 | std::unordered_set<std::string>(paths.begin(), paths.end()); | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 468 | return ipmiDecoratorPaths; | 
|  | 469 | } | 
|  | 470 |  | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 471 | const std::string* getSensorConfigurationInterface( | 
|  | 472 | const std::map<std::string, std::vector<std::string>>& | 
|  | 473 | sensorInterfacesResponse) | 
|  | 474 | { | 
|  | 475 | auto entityManagerService = | 
|  | 476 | sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager"); | 
|  | 477 | if (entityManagerService == sensorInterfacesResponse.end()) | 
|  | 478 | { | 
|  | 479 | return nullptr; | 
|  | 480 | } | 
|  | 481 |  | 
|  | 482 | // Find the fan configuration first (fans can have multiple configuration | 
|  | 483 | // interfaces). | 
|  | 484 | for (const auto& entry : entityManagerService->second) | 
|  | 485 | { | 
|  | 486 | if (entry == "xyz.openbmc_project.Configuration.AspeedFan" || | 
|  | 487 | entry == "xyz.openbmc_project.Configuration.I2CFan" || | 
|  | 488 | entry == "xyz.openbmc_project.Configuration.NuvotonFan") | 
|  | 489 | { | 
|  | 490 | return &entry; | 
|  | 491 | } | 
|  | 492 | } | 
|  | 493 |  | 
|  | 494 | for (const auto& entry : entityManagerService->second) | 
|  | 495 | { | 
|  | 496 | if (boost::algorithm::starts_with(entry, | 
|  | 497 | "xyz.openbmc_project.Configuration.")) | 
|  | 498 | { | 
|  | 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 Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 508 | void 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 Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 513 | { | 
|  | 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 Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 564 | 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 Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 572 |  | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 573 | 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 Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 585 | } | 
|  | 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 Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 600 | std::optional<std::map<std::string, std::vector<std::string>>> | 
|  | 601 | sensorInterfacesResponseOpt = | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 602 | getObjectInterfaces(sensorConfigPath.c_str()); | 
|  | 603 |  | 
| Alexander Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 604 | if (!sensorInterfacesResponseOpt.has_value()) | 
|  | 605 | { | 
| George Liu | de6694e | 2024-07-17 15:22:25 +0800 | [diff] [blame] | 606 | lg2::debug("Failed to GetObject, path: {PATH}", "PATH", | 
|  | 607 | sensorConfigPath); | 
| Alexander Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 608 | continue; | 
|  | 609 | } | 
|  | 610 |  | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 611 | const std::string* configurationInterface = | 
| Alexander Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 612 | getSensorConfigurationInterface( | 
|  | 613 | sensorInterfacesResponseOpt.value()); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 614 |  | 
| Harvey Wu | 7bb84db | 2023-06-26 10:02:08 +0800 | [diff] [blame] | 615 | // 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 Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 618 | if (!configurationInterface) | 
|  | 619 | { | 
| Harvey Wu | 7bb84db | 2023-06-26 10:02:08 +0800 | [diff] [blame] | 620 | continue; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 621 | } | 
|  | 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 |