blob: 753c0ddacc22c6dcd2e56f40315bb35894d2889f [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 <boost/algorithm/string.hpp>
18#include <boost/bimap.hpp>
19#include <boost/container/flat_map.hpp>
20#include <cstdio>
21#include <cstring>
22#include <exception>
23#include <filesystem>
24#include <ipmid/api.hpp>
25#include <ipmid/types.hpp>
26#include <map>
27#include <phosphor-logging/log.hpp>
28#include <sdbusplus/bus/match.hpp>
29#include <string>
30#include <vector>
31
32#pragma once
33
34static constexpr bool debug = false;
35
36struct CmpStrVersion
37{
38 bool operator()(std::string a, std::string b) const
39 {
40 return strverscmp(a.c_str(), b.c_str()) < 0;
41 }
42};
43
44using SensorSubTree = boost::container::flat_map<
45 std::string,
46 boost::container::flat_map<std::string, std::vector<std::string>>,
47 CmpStrVersion>;
48
49using SensorNumMap = boost::bimap<int, std::string>;
50
51static constexpr uint16_t maxSensorsPerLUN = 255;
52static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3);
53static constexpr uint16_t lun1Sensor0 = 0x100;
54static constexpr uint16_t lun3Sensor0 = 0x300;
55static constexpr uint16_t invalidSensorNumber = 0xFFFF;
56static constexpr uint8_t reservedSensorNumber = 0xFF;
57
58namespace details
59{
Josh Lehana55c9532020-10-28 21:59:06 -070060// Enable/disable the logging of stats instrumentation
61static constexpr bool enableInstrumentation = false;
62
63class IPMIStatsEntry
64{
65 private:
66 int numReadings = 0;
67 int numMissings = 0;
68 int numStreakRead = 0;
69 int numStreakMiss = 0;
70 double minValue = 0.0;
71 double maxValue = 0.0;
72 std::string sensorName;
73
74 public:
75 const std::string& getName(void) const
76 {
77 return sensorName;
78 }
79
80 void updateName(std::string_view name)
81 {
82 sensorName = name;
83 }
84
85 // Returns true if this is the first successful reading
86 // This is so the caller can log the coefficients used
87 bool updateReading(double reading, int raw)
88 {
89 if constexpr (!enableInstrumentation)
90 {
91 return false;
92 }
93
94 bool first = ((numReadings == 0) && (numMissings == 0));
95
96 // Sensors can use "nan" to indicate unavailable reading
97 if (!(std::isfinite(reading)))
98 {
99 // Only show this if beginning a new streak
100 if (numStreakMiss == 0)
101 {
102 std::cerr << "IPMI sensor " << sensorName
103 << ": Missing reading, byte=" << raw
104 << ", Reading counts good=" << numReadings
105 << " miss=" << numMissings
106 << ", Prior good streak=" << numStreakRead << "\n";
107 }
108
109 numStreakRead = 0;
110 ++numMissings;
111 ++numStreakMiss;
112
113 return first;
114 }
115
116 // Only show this if beginning a new streak and not the first time
117 if ((numStreakRead == 0) && (numReadings != 0))
118 {
119 std::cerr << "IPMI sensor " << sensorName
120 << ": Recovered reading, value=" << reading
121 << " byte=" << raw
122 << ", Reading counts good=" << numReadings
123 << " miss=" << numMissings
124 << ", Prior miss streak=" << numStreakMiss << "\n";
125 }
126
127 // Initialize min/max if the first successful reading
128 if (numReadings == 0)
129 {
130 std::cerr << "IPMI sensor " << sensorName
131 << ": First reading, value=" << reading << " byte=" << raw
132 << "\n";
133
134 minValue = reading;
135 maxValue = reading;
136 }
137
138 numStreakMiss = 0;
139 ++numReadings;
140 ++numStreakRead;
141
142 // Only provide subsequent output if new min/max established
143 if (reading < minValue)
144 {
145 std::cerr << "IPMI sensor " << sensorName
146 << ": Lowest reading, value=" << reading
147 << " byte=" << raw << "\n";
148
149 minValue = reading;
150 }
151
152 if (reading > maxValue)
153 {
154 std::cerr << "IPMI sensor " << sensorName
155 << ": Highest reading, value=" << reading
156 << " byte=" << raw << "\n";
157
158 maxValue = reading;
159 }
160
161 return first;
162 }
163};
164
165class IPMIStatsTable
166{
167 private:
168 std::vector<IPMIStatsEntry> entries;
169
170 private:
171 void padEntries(size_t index)
172 {
173 char hexbuf[16];
174
175 // Pad vector until entries[index] becomes a valid index
176 while (entries.size() <= index)
177 {
178 // As name not known yet, use human-readable hex as name
179 IPMIStatsEntry newEntry;
180 sprintf(hexbuf, "0x%02zX", entries.size());
181 newEntry.updateName(hexbuf);
182
183 entries.push_back(std::move(newEntry));
184 }
185 }
186
187 public:
188 void wipeTable(void)
189 {
190 entries.clear();
191 }
192
193 const std::string& getName(size_t index)
194 {
195 padEntries(index);
196 return entries[index].getName();
197 }
198
199 void updateName(size_t index, std::string_view name)
200 {
201 padEntries(index);
202 entries[index].updateName(name);
203 }
204
205 bool updateReading(size_t index, double reading, int raw)
206 {
207 padEntries(index);
208 return entries[index].updateReading(reading, raw);
209 }
210};
211
Hao Jiangd2afd052020-12-10 15:09:32 -0800212// Store information for threshold sensors and they are not used by VR
213// sensors. These objects are global singletons, used from a variety of places.
Josh Lehana55c9532020-10-28 21:59:06 -0700214inline IPMIStatsTable sdrStatsTable;
215
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800216/**
217 * Search ObjectMapper for sensors and update them to subtree.
218 *
219 * The function will search for sensors under either
220 * /xyz/openbmc_project/sensors or /xyz/openbmc_project/extsensors. It will
221 * optionally search VR typed sensors under /xyz/openbmc_project/vr
222 *
223 * @return the updated amount of times any of "sensors" or "extsensors" sensor
224 * paths updated successfully, previous amount if all failed. The "vr"
225 * sensor path is optional, and does not participate in the return value.
226 */
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800227uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree);
Willy Tude54f482021-01-26 15:59:09 -0800228
229bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap);
230} // namespace details
231
232bool getSensorSubtree(SensorSubTree& subtree);
233
234struct CmpStr
235{
236 bool operator()(const char* a, const char* b) const
237 {
238 return std::strcmp(a, b) < 0;
239 }
240};
241
242enum class SensorTypeCodes : uint8_t
243{
244 reserved = 0x0,
245 temperature = 0x1,
246 voltage = 0x2,
247 current = 0x3,
248 fan = 0x4,
249 other = 0xB,
250};
251
252const static boost::container::flat_map<const char*, SensorTypeCodes, CmpStr>
253 sensorTypes{{{"temperature", SensorTypeCodes::temperature},
254 {"voltage", SensorTypeCodes::voltage},
255 {"current", SensorTypeCodes::current},
256 {"fan_tach", SensorTypeCodes::fan},
257 {"fan_pwm", SensorTypeCodes::fan},
258 {"power", SensorTypeCodes::other}}};
259
260std::string getSensorTypeStringFromPath(const std::string& path);
261
262uint8_t getSensorTypeFromPath(const std::string& path);
263
264uint16_t getSensorNumberFromPath(const std::string& path);
265
266uint8_t getSensorEventTypeFromPath(const std::string& path);
267
268std::string getPathFromSensorNumber(uint16_t sensorNum);
269
270namespace ipmi
271{
272std::map<std::string, std::vector<std::string>>
273 getObjectInterfaces(const char* path);
274
275std::map<std::string, Value> getEntityManagerProperties(const char* path,
276 const char* interface);
277
278const std::string* getSensorConfigurationInterface(
279 const std::map<std::string, std::vector<std::string>>&
280 sensorInterfacesResponse);
281
282void updateIpmiFromAssociation(const std::string& path,
283 const DbusInterfaceMap& sensorMap,
284 uint8_t& entityId, uint8_t& entityInstance);
285} // namespace ipmi