| 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. | 
| George Liu | c024b39 | 2025-08-21 16:38:58 +0800 | [diff] [blame] | 188 |             if (sensor.second.sensorPath.starts_with( | 
 | 189 |                     "/xyz/openbmc_project/sensors/")) | 
| Scron Chang | 2703b02 | 2021-07-06 15:47:45 +0800 | [diff] [blame] | 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 |     { | 
| George Liu | c024b39 | 2025-08-21 16:38:58 +0800 | [diff] [blame] | 496 |         if (entry.starts_with("xyz.openbmc_project.Configuration.")) | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 497 |         { | 
 | 498 |             return &entry; | 
 | 499 |         } | 
 | 500 |     } | 
 | 501 |  | 
 | 502 |     return nullptr; | 
 | 503 | } | 
 | 504 |  | 
 | 505 | // Follow Association properties for Sensor back to the Board dbus object to | 
 | 506 | // check for an EntityId and EntityInstance property. | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 507 | void updateIpmiFromAssociation( | 
 | 508 |     const std::string& path, | 
 | 509 |     const std::unordered_set<std::string>& ipmiDecoratorPaths, | 
 | 510 |     const DbusInterfaceMap& sensorMap, uint8_t& entityId, | 
 | 511 |     uint8_t& entityInstance) | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 512 | { | 
 | 513 |     namespace fs = std::filesystem; | 
 | 514 |  | 
 | 515 |     auto sensorAssociationObject = | 
 | 516 |         sensorMap.find("xyz.openbmc_project.Association.Definitions"); | 
 | 517 |     if (sensorAssociationObject == sensorMap.end()) | 
 | 518 |     { | 
 | 519 |         if constexpr (debug) | 
 | 520 |         { | 
 | 521 |             std::fprintf(stderr, "path=%s, no association interface found\n", | 
 | 522 |                          path.c_str()); | 
 | 523 |         } | 
 | 524 |  | 
 | 525 |         return; | 
 | 526 |     } | 
 | 527 |  | 
 | 528 |     auto associationObject = | 
 | 529 |         sensorAssociationObject->second.find("Associations"); | 
 | 530 |     if (associationObject == sensorAssociationObject->second.end()) | 
 | 531 |     { | 
 | 532 |         if constexpr (debug) | 
 | 533 |         { | 
 | 534 |             std::fprintf(stderr, "path=%s, no association records found\n", | 
 | 535 |                          path.c_str()); | 
 | 536 |         } | 
 | 537 |  | 
 | 538 |         return; | 
 | 539 |     } | 
 | 540 |  | 
 | 541 |     std::vector<Association> associationValues = | 
 | 542 |         std::get<std::vector<Association>>(associationObject->second); | 
 | 543 |  | 
 | 544 |     // loop through the Associations looking for the right one: | 
 | 545 |     for (const auto& entry : associationValues) | 
 | 546 |     { | 
 | 547 |         // forward, reverse, endpoint | 
 | 548 |         const std::string& forward = std::get<0>(entry); | 
 | 549 |         const std::string& reverse = std::get<1>(entry); | 
 | 550 |         const std::string& endpoint = std::get<2>(entry); | 
 | 551 |  | 
 | 552 |         // We only currently concern ourselves with chassis+all_sensors. | 
 | 553 |         if (!(forward == "chassis" && reverse == "all_sensors")) | 
 | 554 |         { | 
 | 555 |             continue; | 
 | 556 |         } | 
 | 557 |  | 
 | 558 |         // the endpoint is the board entry provided by | 
 | 559 |         // Entity-Manager. so let's grab its properties if it has | 
 | 560 |         // the right interface. | 
 | 561 |  | 
 | 562 |         // just try grabbing the properties first. | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 563 |         ipmi::PropertyMap::iterator entityIdProp; | 
 | 564 |         ipmi::PropertyMap::iterator entityInstanceProp; | 
 | 565 |         if (ipmiDecoratorPaths.contains(endpoint)) | 
 | 566 |         { | 
 | 567 |             std::map<std::string, Value> ipmiProperties = | 
 | 568 |                 getEntityManagerProperties( | 
 | 569 |                     endpoint.c_str(), | 
 | 570 |                     "xyz.openbmc_project.Inventory.Decorator.Ipmi"); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 571 |  | 
| Willy Tu | 4eca251 | 2022-06-20 21:14:51 -0700 | [diff] [blame] | 572 |             entityIdProp = ipmiProperties.find("EntityId"); | 
 | 573 |             entityInstanceProp = ipmiProperties.find("EntityInstance"); | 
 | 574 |             if (entityIdProp != ipmiProperties.end()) | 
 | 575 |             { | 
 | 576 |                 entityId = static_cast<uint8_t>( | 
 | 577 |                     std::get<uint64_t>(entityIdProp->second)); | 
 | 578 |             } | 
 | 579 |             if (entityInstanceProp != ipmiProperties.end()) | 
 | 580 |             { | 
 | 581 |                 entityInstance = static_cast<uint8_t>( | 
 | 582 |                     std::get<uint64_t>(entityInstanceProp->second)); | 
 | 583 |             } | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 584 |         } | 
 | 585 |  | 
 | 586 |         // Now check the entity-manager entry for this sensor to see | 
 | 587 |         // if it has its own value and use that instead. | 
 | 588 |         // | 
 | 589 |         // In theory, checking this first saves us from checking | 
 | 590 |         // both, except in most use-cases identified, there won't be | 
 | 591 |         // a per sensor override, so we need to always check both. | 
 | 592 |         std::string sensorNameFromPath = fs::path(path).filename(); | 
 | 593 |  | 
 | 594 |         std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath; | 
 | 595 |  | 
 | 596 |         // Download the interfaces for the sensor from | 
 | 597 |         // Entity-Manager to find the name of the configuration | 
 | 598 |         // interface. | 
| Alexander Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 599 |         std::optional<std::map<std::string, std::vector<std::string>>> | 
 | 600 |             sensorInterfacesResponseOpt = | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 601 |                 getObjectInterfaces(sensorConfigPath.c_str()); | 
 | 602 |  | 
| Alexander Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 603 |         if (!sensorInterfacesResponseOpt.has_value()) | 
 | 604 |         { | 
| George Liu | de6694e | 2024-07-17 15:22:25 +0800 | [diff] [blame] | 605 |             lg2::debug("Failed to GetObject, path: {PATH}", "PATH", | 
 | 606 |                        sensorConfigPath); | 
| Alexander Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 607 |             continue; | 
 | 608 |         } | 
 | 609 |  | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 610 |         const std::string* configurationInterface = | 
| Alexander Hansen | 8fb5b89 | 2023-11-02 17:29:34 +0100 | [diff] [blame] | 611 |             getSensorConfigurationInterface( | 
 | 612 |                 sensorInterfacesResponseOpt.value()); | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 613 |  | 
| Harvey Wu | 7bb84db | 2023-06-26 10:02:08 +0800 | [diff] [blame] | 614 |         // If there are multi association path settings and only one path exist, | 
 | 615 |         // we need to continue if cannot find configuration interface for this | 
 | 616 |         // sensor. | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 617 |         if (!configurationInterface) | 
 | 618 |         { | 
| Harvey Wu | 7bb84db | 2023-06-26 10:02:08 +0800 | [diff] [blame] | 619 |             continue; | 
| Willy Tu | de54f48 | 2021-01-26 15:59:09 -0800 | [diff] [blame] | 620 |         } | 
 | 621 |  | 
 | 622 |         // We found a configuration interface. | 
 | 623 |         std::map<std::string, Value> configurationProperties = | 
 | 624 |             getEntityManagerProperties(sensorConfigPath.c_str(), | 
 | 625 |                                        configurationInterface->c_str()); | 
 | 626 |  | 
 | 627 |         entityIdProp = configurationProperties.find("EntityId"); | 
 | 628 |         entityInstanceProp = configurationProperties.find("EntityInstance"); | 
 | 629 |         if (entityIdProp != configurationProperties.end()) | 
 | 630 |         { | 
 | 631 |             entityId = | 
 | 632 |                 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second)); | 
 | 633 |         } | 
 | 634 |         if (entityInstanceProp != configurationProperties.end()) | 
 | 635 |         { | 
 | 636 |             entityInstance = static_cast<uint8_t>( | 
 | 637 |                 std::get<uint64_t>(entityInstanceProp->second)); | 
 | 638 |         } | 
 | 639 |  | 
 | 640 |         // stop searching Association records. | 
 | 641 |         break; | 
 | 642 |     } // end for Association vectors. | 
 | 643 |  | 
 | 644 |     if constexpr (debug) | 
 | 645 |     { | 
 | 646 |         std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n", | 
 | 647 |                      path.c_str(), entityId, entityInstance); | 
 | 648 |     } | 
 | 649 | } | 
 | 650 |  | 
 | 651 | } // namespace ipmi |