blob: 3933245eacb3f287ac64c64367b13b72a9596090 [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
Scron Chang2703b022021-07-06 15:47:45 +0800234#ifdef FEATURE_HYBRID_SENSORS
235ipmi::sensor::IdInfoMap::const_iterator
236 findStaticSensor(const std::string& path);
237#endif
238
Willy Tude54f482021-01-26 15:59:09 -0800239struct CmpStr
240{
241 bool operator()(const char* a, const char* b) const
242 {
243 return std::strcmp(a, b) < 0;
244 }
245};
246
Scron Chang2b42d7e2021-07-06 15:45:47 +0800247static constexpr size_t sensorTypeCodes = 0;
248static constexpr size_t sensorEventTypeCodes = 1;
249
Willy Tude54f482021-01-26 15:59:09 -0800250enum class SensorTypeCodes : uint8_t
251{
252 reserved = 0x0,
253 temperature = 0x1,
254 voltage = 0x2,
255 current = 0x3,
256 fan = 0x4,
257 other = 0xB,
Scron Changb8e5b162021-07-06 15:46:43 +0800258 memory = 0x0c,
259 power_unit = 0x09,
260 buttons = 0x14,
261 watchdog2 = 0x23,
Willy Tude54f482021-01-26 15:59:09 -0800262};
263
Scron Chang2b42d7e2021-07-06 15:45:47 +0800264enum class SensorEventTypeCodes : uint8_t
265{
266 unspecified = 0x00,
267 threshold = 0x01,
268 sensorSpecified = 0x6f
269};
270
271const static boost::container::flat_map<
272 const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr>
273 sensorTypes{
274 {{"temperature", std::make_pair(SensorTypeCodes::temperature,
275 SensorEventTypeCodes::threshold)},
276 {"voltage", std::make_pair(SensorTypeCodes::voltage,
277 SensorEventTypeCodes::threshold)},
278 {"current", std::make_pair(SensorTypeCodes::current,
279 SensorEventTypeCodes::threshold)},
280 {"fan_tach", std::make_pair(SensorTypeCodes::fan,
281 SensorEventTypeCodes::threshold)},
282 {"fan_pwm", std::make_pair(SensorTypeCodes::fan,
283 SensorEventTypeCodes::threshold)},
284 {"power", std::make_pair(SensorTypeCodes::other,
Scron Changb8e5b162021-07-06 15:46:43 +0800285 SensorEventTypeCodes::threshold)},
286 {"memory", std::make_pair(SensorTypeCodes::memory,
287 SensorEventTypeCodes::sensorSpecified)},
288 {"state", std::make_pair(SensorTypeCodes::power_unit,
289 SensorEventTypeCodes::sensorSpecified)},
290 {"buttons", std::make_pair(SensorTypeCodes::buttons,
291 SensorEventTypeCodes::sensorSpecified)},
292 {"watchdog", std::make_pair(SensorTypeCodes::watchdog2,
293 SensorEventTypeCodes::sensorSpecified)}}};
Willy Tude54f482021-01-26 15:59:09 -0800294
295std::string getSensorTypeStringFromPath(const std::string& path);
296
297uint8_t getSensorTypeFromPath(const std::string& path);
298
299uint16_t getSensorNumberFromPath(const std::string& path);
300
301uint8_t getSensorEventTypeFromPath(const std::string& path);
302
303std::string getPathFromSensorNumber(uint16_t sensorNum);
304
305namespace ipmi
306{
307std::map<std::string, std::vector<std::string>>
308 getObjectInterfaces(const char* path);
309
310std::map<std::string, Value> getEntityManagerProperties(const char* path,
311 const char* interface);
312
313const std::string* getSensorConfigurationInterface(
314 const std::map<std::string, std::vector<std::string>>&
315 sensorInterfacesResponse);
316
317void updateIpmiFromAssociation(const std::string& path,
318 const DbusInterfaceMap& sensorMap,
319 uint8_t& entityId, uint8_t& entityInstance);
320} // namespace ipmi