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