blob: 1eadb6f676c99980f84a2c199c1078252472b690 [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
19namespace details
20{
Kuiying Wanga8b5b262021-02-06 23:38:22 +080021uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree)
Willy Tude54f482021-01-26 15:59:09 -080022{
23 static std::shared_ptr<SensorSubTree> sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +080024 static uint16_t sensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -080025 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
26 static sdbusplus::bus::match::match sensorAdded(
27 *dbus,
28 "type='signal',member='InterfacesAdded',arg0path='/xyz/openbmc_project/"
29 "sensors/'",
30 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
31
32 static sdbusplus::bus::match::match sensorRemoved(
33 *dbus,
34 "type='signal',member='InterfacesRemoved',arg0path='/xyz/"
35 "openbmc_project/sensors/'",
36 [](sdbusplus::message::message& m) { sensorTreePtr.reset(); });
37
Willy Tude54f482021-01-26 15:59:09 -080038 if (sensorTreePtr)
39 {
40 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +080041 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -080042 }
43
44 sensorTreePtr = std::make_shared<SensorSubTree>();
45
46 auto mapperCall =
47 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
48 "/xyz/openbmc_project/object_mapper",
49 "xyz.openbmc_project.ObjectMapper", "GetSubTree");
50 static constexpr const int32_t depth = 2;
51 static constexpr std::array<const char*, 3> interfaces = {
52 "xyz.openbmc_project.Sensor.Value",
53 "xyz.openbmc_project.Sensor.Threshold.Warning",
54 "xyz.openbmc_project.Sensor.Threshold.Critical"};
55 mapperCall.append("/xyz/openbmc_project/sensors", depth, interfaces);
56
57 try
58 {
59 auto mapperReply = dbus->call(mapperCall);
60 mapperReply.read(*sensorTreePtr);
61 }
62 catch (sdbusplus::exception_t& e)
63 {
64 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
Kuiying Wanga8b5b262021-02-06 23:38:22 +080065 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -080066 }
67 subtree = sensorTreePtr;
Kuiying Wanga8b5b262021-02-06 23:38:22 +080068 sensorUpdatedIndex++;
Josh Lehana55c9532020-10-28 21:59:06 -070069 // The SDR is being regenerated, wipe the old stats
70 sdrStatsTable.wipeTable();
Kuiying Wanga8b5b262021-02-06 23:38:22 +080071 return sensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -080072}
73
74bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap)
75{
76 static std::shared_ptr<SensorNumMap> sensorNumMapPtr;
77 bool sensorNumMapUpated = false;
Kuiying Wanga8b5b262021-02-06 23:38:22 +080078 static uint16_t prevSensorUpdatedIndex = 0;
Willy Tude54f482021-01-26 15:59:09 -080079 std::shared_ptr<SensorSubTree> sensorTree;
Kuiying Wanga8b5b262021-02-06 23:38:22 +080080 uint16_t curSensorUpdatedIndex = details::getSensorSubtree(sensorTree);
Willy Tude54f482021-01-26 15:59:09 -080081 if (!sensorTree)
82 {
83 return sensorNumMapUpated;
84 }
85
Kuiying Wanga8b5b262021-02-06 23:38:22 +080086 if ((curSensorUpdatedIndex == prevSensorUpdatedIndex) && sensorNumMapPtr)
Willy Tude54f482021-01-26 15:59:09 -080087 {
88 sensorNumMap = sensorNumMapPtr;
89 return sensorNumMapUpated;
90 }
Kuiying Wanga8b5b262021-02-06 23:38:22 +080091 prevSensorUpdatedIndex = curSensorUpdatedIndex;
Willy Tude54f482021-01-26 15:59:09 -080092
93 sensorNumMapPtr = std::make_shared<SensorNumMap>();
94
95 uint16_t sensorNum = 0;
96 uint16_t sensorIndex = 0;
97 for (const auto& sensor : *sensorTree)
98 {
99 sensorNumMapPtr->insert(
100 SensorNumMap::value_type(sensorNum, sensor.first));
101 sensorIndex++;
102 if (sensorIndex == maxSensorsPerLUN)
103 {
104 sensorIndex = lun1Sensor0;
105 }
106 else if (sensorIndex == (lun1Sensor0 | maxSensorsPerLUN))
107 {
108 // Skip assigning LUN 0x2 any sensors
109 sensorIndex = lun3Sensor0;
110 }
111 else if (sensorIndex == (lun3Sensor0 | maxSensorsPerLUN))
112 {
113 // this is an error, too many IPMI sensors
114 throw std::out_of_range("Maximum number of IPMI sensors exceeded.");
115 }
116 sensorNum = sensorIndex;
117 }
118 sensorNumMap = sensorNumMapPtr;
119 sensorNumMapUpated = true;
120 return sensorNumMapUpated;
121}
122} // namespace details
123
124bool getSensorSubtree(SensorSubTree& subtree)
125{
126 std::shared_ptr<SensorSubTree> sensorTree;
127 details::getSensorSubtree(sensorTree);
128 if (!sensorTree)
129 {
130 return false;
131 }
132
133 subtree = *sensorTree;
134 return true;
135}
136
137std::string getSensorTypeStringFromPath(const std::string& path)
138{
139 // get sensor type string from path, path is defined as
140 // /xyz/openbmc_project/sensors/<type>/label
141 size_t typeEnd = path.rfind("/");
142 if (typeEnd == std::string::npos)
143 {
144 return path;
145 }
146 size_t typeStart = path.rfind("/", typeEnd - 1);
147 if (typeStart == std::string::npos)
148 {
149 return path;
150 }
151 // Start at the character after the '/'
152 typeStart++;
153 return path.substr(typeStart, typeEnd - typeStart);
154}
155
156uint8_t getSensorTypeFromPath(const std::string& path)
157{
158 uint8_t sensorType = 0;
159 std::string type = getSensorTypeStringFromPath(path);
160 auto findSensor = sensorTypes.find(type.c_str());
161 if (findSensor != sensorTypes.end())
162 {
163 sensorType = static_cast<uint8_t>(findSensor->second);
164 } // else default 0x0 RESERVED
165
166 return sensorType;
167}
168
169uint16_t getSensorNumberFromPath(const std::string& path)
170{
171 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
172 details::getSensorNumMap(sensorNumMapPtr);
173 if (!sensorNumMapPtr)
174 {
175 return invalidSensorNumber;
176 }
177
178 try
179 {
180 return sensorNumMapPtr->right.at(path);
181 }
182 catch (std::out_of_range& e)
183 {
184 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
185 return invalidSensorNumber;
186 }
187}
188
189uint8_t getSensorEventTypeFromPath(const std::string& path)
190{
191 // TODO: Add support for additional reading types as needed
192 return 0x1; // reading type = threshold
193}
194
195std::string getPathFromSensorNumber(uint16_t sensorNum)
196{
197 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
198 details::getSensorNumMap(sensorNumMapPtr);
199 if (!sensorNumMapPtr)
200 {
201 return std::string();
202 }
203
204 try
205 {
206 return sensorNumMapPtr->left.at(sensorNum);
207 }
208 catch (std::out_of_range& e)
209 {
210 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
211 return std::string();
212 }
213}
214
215namespace ipmi
216{
217
218std::map<std::string, std::vector<std::string>>
219 getObjectInterfaces(const char* path)
220{
221 std::map<std::string, std::vector<std::string>> interfacesResponse;
222 std::vector<std::string> interfaces;
223 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
224
225 sdbusplus::message::message getObjectMessage =
226 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
227 "/xyz/openbmc_project/object_mapper",
228 "xyz.openbmc_project.ObjectMapper", "GetObject");
229 getObjectMessage.append(path, interfaces);
230
231 try
232 {
233 sdbusplus::message::message response = dbus->call(getObjectMessage);
234 response.read(interfacesResponse);
235 }
236 catch (const std::exception& e)
237 {
238 phosphor::logging::log<phosphor::logging::level::ERR>(
239 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
240 phosphor::logging::entry("WHAT=%s", e.what()));
241 }
242
243 return interfacesResponse;
244}
245
246std::map<std::string, Value> getEntityManagerProperties(const char* path,
247 const char* interface)
248{
249 std::map<std::string, Value> properties;
250 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
251
252 sdbusplus::message::message getProperties =
253 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
254 "org.freedesktop.DBus.Properties", "GetAll");
255 getProperties.append(interface);
256
257 try
258 {
259 sdbusplus::message::message response = dbus->call(getProperties);
260 response.read(properties);
261 }
262 catch (const std::exception& e)
263 {
264 phosphor::logging::log<phosphor::logging::level::ERR>(
265 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
266 phosphor::logging::entry("INTF=%s", interface),
267 phosphor::logging::entry("WHAT=%s", e.what()));
268 }
269
270 return properties;
271}
272
273const std::string* getSensorConfigurationInterface(
274 const std::map<std::string, std::vector<std::string>>&
275 sensorInterfacesResponse)
276{
277 auto entityManagerService =
278 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
279 if (entityManagerService == sensorInterfacesResponse.end())
280 {
281 return nullptr;
282 }
283
284 // Find the fan configuration first (fans can have multiple configuration
285 // interfaces).
286 for (const auto& entry : entityManagerService->second)
287 {
288 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
289 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
290 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
291 {
292 return &entry;
293 }
294 }
295
296 for (const auto& entry : entityManagerService->second)
297 {
298 if (boost::algorithm::starts_with(entry,
299 "xyz.openbmc_project.Configuration."))
300 {
301 return &entry;
302 }
303 }
304
305 return nullptr;
306}
307
308// Follow Association properties for Sensor back to the Board dbus object to
309// check for an EntityId and EntityInstance property.
310void updateIpmiFromAssociation(const std::string& path,
311 const DbusInterfaceMap& sensorMap,
312 uint8_t& entityId, uint8_t& entityInstance)
313{
314 namespace fs = std::filesystem;
315
316 auto sensorAssociationObject =
317 sensorMap.find("xyz.openbmc_project.Association.Definitions");
318 if (sensorAssociationObject == sensorMap.end())
319 {
320 if constexpr (debug)
321 {
322 std::fprintf(stderr, "path=%s, no association interface found\n",
323 path.c_str());
324 }
325
326 return;
327 }
328
329 auto associationObject =
330 sensorAssociationObject->second.find("Associations");
331 if (associationObject == sensorAssociationObject->second.end())
332 {
333 if constexpr (debug)
334 {
335 std::fprintf(stderr, "path=%s, no association records found\n",
336 path.c_str());
337 }
338
339 return;
340 }
341
342 std::vector<Association> associationValues =
343 std::get<std::vector<Association>>(associationObject->second);
344
345 // loop through the Associations looking for the right one:
346 for (const auto& entry : associationValues)
347 {
348 // forward, reverse, endpoint
349 const std::string& forward = std::get<0>(entry);
350 const std::string& reverse = std::get<1>(entry);
351 const std::string& endpoint = std::get<2>(entry);
352
353 // We only currently concern ourselves with chassis+all_sensors.
354 if (!(forward == "chassis" && reverse == "all_sensors"))
355 {
356 continue;
357 }
358
359 // the endpoint is the board entry provided by
360 // Entity-Manager. so let's grab its properties if it has
361 // the right interface.
362
363 // just try grabbing the properties first.
364 std::map<std::string, Value> ipmiProperties =
365 getEntityManagerProperties(
366 endpoint.c_str(),
367 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
368
369 auto entityIdProp = ipmiProperties.find("EntityId");
370 auto entityInstanceProp = ipmiProperties.find("EntityInstance");
371 if (entityIdProp != ipmiProperties.end())
372 {
373 entityId =
374 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
375 }
376 if (entityInstanceProp != ipmiProperties.end())
377 {
378 entityInstance = static_cast<uint8_t>(
379 std::get<uint64_t>(entityInstanceProp->second));
380 }
381
382 // Now check the entity-manager entry for this sensor to see
383 // if it has its own value and use that instead.
384 //
385 // In theory, checking this first saves us from checking
386 // both, except in most use-cases identified, there won't be
387 // a per sensor override, so we need to always check both.
388 std::string sensorNameFromPath = fs::path(path).filename();
389
390 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
391
392 // Download the interfaces for the sensor from
393 // Entity-Manager to find the name of the configuration
394 // interface.
395 std::map<std::string, std::vector<std::string>>
396 sensorInterfacesResponse =
397 getObjectInterfaces(sensorConfigPath.c_str());
398
399 const std::string* configurationInterface =
400 getSensorConfigurationInterface(sensorInterfacesResponse);
401
402 // We didnt' find a configuration interface for this sensor, but we
403 // followed the Association property to get here, so we're done
404 // searching.
405 if (!configurationInterface)
406 {
407 break;
408 }
409
410 // We found a configuration interface.
411 std::map<std::string, Value> configurationProperties =
412 getEntityManagerProperties(sensorConfigPath.c_str(),
413 configurationInterface->c_str());
414
415 entityIdProp = configurationProperties.find("EntityId");
416 entityInstanceProp = configurationProperties.find("EntityInstance");
417 if (entityIdProp != configurationProperties.end())
418 {
419 entityId =
420 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
421 }
422 if (entityInstanceProp != configurationProperties.end())
423 {
424 entityInstance = static_cast<uint8_t>(
425 std::get<uint64_t>(entityInstanceProp->second));
426 }
427
428 // stop searching Association records.
429 break;
430 } // end for Association vectors.
431
432 if constexpr (debug)
433 {
434 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
435 path.c_str(), entityId, entityInstance);
436 }
437}
438
439} // namespace ipmi