blob: 5ea88d8a176f3bf44574e6f11b4c3562c5e5eda5 [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 {
Scron Chang2b42d7e2021-07-06 15:45:47 +0800196 sensorType =
197 static_cast<uint8_t>(std::get<sensorTypeCodes>(findSensor->second));
Willy Tude54f482021-01-26 15:59:09 -0800198 } // else default 0x0 RESERVED
199
200 return sensorType;
201}
202
203uint16_t getSensorNumberFromPath(const std::string& path)
204{
205 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
206 details::getSensorNumMap(sensorNumMapPtr);
207 if (!sensorNumMapPtr)
208 {
209 return invalidSensorNumber;
210 }
211
212 try
213 {
214 return sensorNumMapPtr->right.at(path);
215 }
216 catch (std::out_of_range& e)
217 {
218 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
219 return invalidSensorNumber;
220 }
221}
222
223uint8_t getSensorEventTypeFromPath(const std::string& path)
224{
Scron Chang2b42d7e2021-07-06 15:45:47 +0800225 uint8_t sensorEventType = 0;
226 std::string type = getSensorTypeStringFromPath(path);
227 auto findSensor = sensorTypes.find(type.c_str());
228 if (findSensor != sensorTypes.end())
229 {
230 sensorEventType = static_cast<uint8_t>(
231 std::get<sensorEventTypeCodes>(findSensor->second));
232 }
233
234 return sensorEventType;
Willy Tude54f482021-01-26 15:59:09 -0800235}
236
237std::string getPathFromSensorNumber(uint16_t sensorNum)
238{
239 std::shared_ptr<SensorNumMap> sensorNumMapPtr;
240 details::getSensorNumMap(sensorNumMapPtr);
241 if (!sensorNumMapPtr)
242 {
243 return std::string();
244 }
245
246 try
247 {
248 return sensorNumMapPtr->left.at(sensorNum);
249 }
250 catch (std::out_of_range& e)
251 {
252 phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
253 return std::string();
254 }
255}
256
257namespace ipmi
258{
259
260std::map<std::string, std::vector<std::string>>
261 getObjectInterfaces(const char* path)
262{
263 std::map<std::string, std::vector<std::string>> interfacesResponse;
264 std::vector<std::string> interfaces;
265 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
266
267 sdbusplus::message::message getObjectMessage =
268 dbus->new_method_call("xyz.openbmc_project.ObjectMapper",
269 "/xyz/openbmc_project/object_mapper",
270 "xyz.openbmc_project.ObjectMapper", "GetObject");
271 getObjectMessage.append(path, interfaces);
272
273 try
274 {
275 sdbusplus::message::message response = dbus->call(getObjectMessage);
276 response.read(interfacesResponse);
277 }
278 catch (const std::exception& e)
279 {
280 phosphor::logging::log<phosphor::logging::level::ERR>(
281 "Failed to GetObject", phosphor::logging::entry("PATH=%s", path),
282 phosphor::logging::entry("WHAT=%s", e.what()));
283 }
284
285 return interfacesResponse;
286}
287
288std::map<std::string, Value> getEntityManagerProperties(const char* path,
289 const char* interface)
290{
291 std::map<std::string, Value> properties;
292 std::shared_ptr<sdbusplus::asio::connection> dbus = getSdBus();
293
294 sdbusplus::message::message getProperties =
295 dbus->new_method_call("xyz.openbmc_project.EntityManager", path,
296 "org.freedesktop.DBus.Properties", "GetAll");
297 getProperties.append(interface);
298
299 try
300 {
301 sdbusplus::message::message response = dbus->call(getProperties);
302 response.read(properties);
303 }
304 catch (const std::exception& e)
305 {
306 phosphor::logging::log<phosphor::logging::level::ERR>(
307 "Failed to GetAll", phosphor::logging::entry("PATH=%s", path),
308 phosphor::logging::entry("INTF=%s", interface),
309 phosphor::logging::entry("WHAT=%s", e.what()));
310 }
311
312 return properties;
313}
314
315const std::string* getSensorConfigurationInterface(
316 const std::map<std::string, std::vector<std::string>>&
317 sensorInterfacesResponse)
318{
319 auto entityManagerService =
320 sensorInterfacesResponse.find("xyz.openbmc_project.EntityManager");
321 if (entityManagerService == sensorInterfacesResponse.end())
322 {
323 return nullptr;
324 }
325
326 // Find the fan configuration first (fans can have multiple configuration
327 // interfaces).
328 for (const auto& entry : entityManagerService->second)
329 {
330 if (entry == "xyz.openbmc_project.Configuration.AspeedFan" ||
331 entry == "xyz.openbmc_project.Configuration.I2CFan" ||
332 entry == "xyz.openbmc_project.Configuration.NuvotonFan")
333 {
334 return &entry;
335 }
336 }
337
338 for (const auto& entry : entityManagerService->second)
339 {
340 if (boost::algorithm::starts_with(entry,
341 "xyz.openbmc_project.Configuration."))
342 {
343 return &entry;
344 }
345 }
346
347 return nullptr;
348}
349
350// Follow Association properties for Sensor back to the Board dbus object to
351// check for an EntityId and EntityInstance property.
352void updateIpmiFromAssociation(const std::string& path,
353 const DbusInterfaceMap& sensorMap,
354 uint8_t& entityId, uint8_t& entityInstance)
355{
356 namespace fs = std::filesystem;
357
358 auto sensorAssociationObject =
359 sensorMap.find("xyz.openbmc_project.Association.Definitions");
360 if (sensorAssociationObject == sensorMap.end())
361 {
362 if constexpr (debug)
363 {
364 std::fprintf(stderr, "path=%s, no association interface found\n",
365 path.c_str());
366 }
367
368 return;
369 }
370
371 auto associationObject =
372 sensorAssociationObject->second.find("Associations");
373 if (associationObject == sensorAssociationObject->second.end())
374 {
375 if constexpr (debug)
376 {
377 std::fprintf(stderr, "path=%s, no association records found\n",
378 path.c_str());
379 }
380
381 return;
382 }
383
384 std::vector<Association> associationValues =
385 std::get<std::vector<Association>>(associationObject->second);
386
387 // loop through the Associations looking for the right one:
388 for (const auto& entry : associationValues)
389 {
390 // forward, reverse, endpoint
391 const std::string& forward = std::get<0>(entry);
392 const std::string& reverse = std::get<1>(entry);
393 const std::string& endpoint = std::get<2>(entry);
394
395 // We only currently concern ourselves with chassis+all_sensors.
396 if (!(forward == "chassis" && reverse == "all_sensors"))
397 {
398 continue;
399 }
400
401 // the endpoint is the board entry provided by
402 // Entity-Manager. so let's grab its properties if it has
403 // the right interface.
404
405 // just try grabbing the properties first.
406 std::map<std::string, Value> ipmiProperties =
407 getEntityManagerProperties(
408 endpoint.c_str(),
409 "xyz.openbmc_project.Inventory.Decorator.Ipmi");
410
411 auto entityIdProp = ipmiProperties.find("EntityId");
412 auto entityInstanceProp = ipmiProperties.find("EntityInstance");
413 if (entityIdProp != ipmiProperties.end())
414 {
415 entityId =
416 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
417 }
418 if (entityInstanceProp != ipmiProperties.end())
419 {
420 entityInstance = static_cast<uint8_t>(
421 std::get<uint64_t>(entityInstanceProp->second));
422 }
423
424 // Now check the entity-manager entry for this sensor to see
425 // if it has its own value and use that instead.
426 //
427 // In theory, checking this first saves us from checking
428 // both, except in most use-cases identified, there won't be
429 // a per sensor override, so we need to always check both.
430 std::string sensorNameFromPath = fs::path(path).filename();
431
432 std::string sensorConfigPath = endpoint + "/" + sensorNameFromPath;
433
434 // Download the interfaces for the sensor from
435 // Entity-Manager to find the name of the configuration
436 // interface.
437 std::map<std::string, std::vector<std::string>>
438 sensorInterfacesResponse =
439 getObjectInterfaces(sensorConfigPath.c_str());
440
441 const std::string* configurationInterface =
442 getSensorConfigurationInterface(sensorInterfacesResponse);
443
444 // We didnt' find a configuration interface for this sensor, but we
445 // followed the Association property to get here, so we're done
446 // searching.
447 if (!configurationInterface)
448 {
449 break;
450 }
451
452 // We found a configuration interface.
453 std::map<std::string, Value> configurationProperties =
454 getEntityManagerProperties(sensorConfigPath.c_str(),
455 configurationInterface->c_str());
456
457 entityIdProp = configurationProperties.find("EntityId");
458 entityInstanceProp = configurationProperties.find("EntityInstance");
459 if (entityIdProp != configurationProperties.end())
460 {
461 entityId =
462 static_cast<uint8_t>(std::get<uint64_t>(entityIdProp->second));
463 }
464 if (entityInstanceProp != configurationProperties.end())
465 {
466 entityInstance = static_cast<uint8_t>(
467 std::get<uint64_t>(entityInstanceProp->second));
468 }
469
470 // stop searching Association records.
471 break;
472 } // end for Association vectors.
473
474 if constexpr (debug)
475 {
476 std::fprintf(stderr, "path=%s, entityId=%d, entityInstance=%d\n",
477 path.c_str(), entityId, entityInstance);
478 }
479}
480
481} // namespace ipmi