xref: /openbmc/phosphor-host-ipmid/include/dbus-sdr/sdrutils.hpp (revision f0a89946f1e3ce2e8fe97525e6b057463aa2a169)
1 /*
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 
34 static constexpr bool debug = false;
35 
36 struct 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 
44 using SensorSubTree = boost::container::flat_map<
45     std::string,
46     boost::container::flat_map<std::string, std::vector<std::string>>,
47     CmpStrVersion>;
48 
49 using SensorNumMap = boost::bimap<int, std::string>;
50 
51 static constexpr uint16_t maxSensorsPerLUN = 255;
52 static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3);
53 static constexpr uint16_t lun1Sensor0 = 0x100;
54 static constexpr uint16_t lun3Sensor0 = 0x300;
55 static constexpr uint16_t invalidSensorNumber = 0xFFFF;
56 static constexpr uint8_t reservedSensorNumber = 0xFF;
57 
58 namespace details
59 {
60 // Enable/disable the logging of stats instrumentation
61 static constexpr bool enableInstrumentation = false;
62 
63 class 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 
165 class 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 
212 class IPMIWriteEntry
213 {
214   private:
215     bool writePermission = false;
216 
217   public:
218     bool getWritePermission(void) const
219     {
220         return writePermission;
221     }
222 
223     void setWritePermission(bool permission)
224     {
225         writePermission = permission;
226     }
227 };
228 
229 class IPMIWriteTable
230 {
231   private:
232     std::vector<IPMIWriteEntry> entries;
233 
234   private:
235     void padEntries(size_t index)
236     {
237         // Pad vector until entries[index] becomes a valid index
238         if (entries.size() <= index)
239         {
240             entries.resize(index + 1);
241         }
242     }
243 
244   public:
245     void wipeTable(void)
246     {
247         entries.clear();
248     }
249 
250     bool getWritePermission(size_t index)
251     {
252         padEntries(index);
253         return entries[index].getWritePermission();
254     }
255 
256     void setWritePermission(size_t index, bool permission)
257     {
258         padEntries(index);
259         entries[index].setWritePermission(permission);
260     }
261 };
262 
263 // Store information for threshold sensors and they are not used by VR
264 // sensors. These objects are global singletons, used from a variety of places.
265 inline IPMIStatsTable sdrStatsTable;
266 inline IPMIWriteTable sdrWriteTable;
267 
268 /**
269  * Search ObjectMapper for sensors and update them to subtree.
270  *
271  * The function will search for sensors under either
272  * /xyz/openbmc_project/sensors or /xyz/openbmc_project/extsensors. It will
273  * optionally search VR typed sensors under /xyz/openbmc_project/vr
274  *
275  * @return the updated amount of times any of "sensors" or "extsensors" sensor
276  * paths updated successfully, previous amount if all failed. The "vr"
277  * sensor path is optional, and does not participate in the return value.
278  */
279 uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree);
280 
281 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap);
282 } // namespace details
283 
284 bool getSensorSubtree(SensorSubTree& subtree);
285 
286 #ifdef FEATURE_HYBRID_SENSORS
287 ipmi::sensor::IdInfoMap::const_iterator
288     findStaticSensor(const std::string& path);
289 #endif
290 
291 struct CmpStr
292 {
293     bool operator()(const char* a, const char* b) const
294     {
295         return std::strcmp(a, b) < 0;
296     }
297 };
298 
299 static constexpr size_t sensorTypeCodes = 0;
300 static constexpr size_t sensorEventTypeCodes = 1;
301 
302 enum class SensorTypeCodes : uint8_t
303 {
304     reserved = 0x0,
305     temperature = 0x1,
306     voltage = 0x2,
307     current = 0x3,
308     fan = 0x4,
309     other = 0xB,
310     memory = 0x0c,
311     power_unit = 0x09,
312     buttons = 0x14,
313     watchdog2 = 0x23,
314 };
315 
316 enum class SensorEventTypeCodes : uint8_t
317 {
318     unspecified = 0x00,
319     threshold = 0x01,
320     sensorSpecified = 0x6f
321 };
322 
323 const static boost::container::flat_map<
324     const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr>
325     sensorTypes{
326         {{"temperature", std::make_pair(SensorTypeCodes::temperature,
327                                         SensorEventTypeCodes::threshold)},
328          {"voltage", std::make_pair(SensorTypeCodes::voltage,
329                                     SensorEventTypeCodes::threshold)},
330          {"current", std::make_pair(SensorTypeCodes::current,
331                                     SensorEventTypeCodes::threshold)},
332          {"fan_tach", std::make_pair(SensorTypeCodes::fan,
333                                      SensorEventTypeCodes::threshold)},
334          {"fan_pwm", std::make_pair(SensorTypeCodes::fan,
335                                     SensorEventTypeCodes::threshold)},
336          {"power", std::make_pair(SensorTypeCodes::other,
337                                   SensorEventTypeCodes::threshold)},
338          {"memory", std::make_pair(SensorTypeCodes::memory,
339                                    SensorEventTypeCodes::sensorSpecified)},
340          {"state", std::make_pair(SensorTypeCodes::power_unit,
341                                   SensorEventTypeCodes::sensorSpecified)},
342          {"buttons", std::make_pair(SensorTypeCodes::buttons,
343                                     SensorEventTypeCodes::sensorSpecified)},
344          {"watchdog", std::make_pair(SensorTypeCodes::watchdog2,
345                                      SensorEventTypeCodes::sensorSpecified)}}};
346 
347 std::string getSensorTypeStringFromPath(const std::string& path);
348 
349 uint8_t getSensorTypeFromPath(const std::string& path);
350 
351 uint16_t getSensorNumberFromPath(const std::string& path);
352 
353 uint8_t getSensorEventTypeFromPath(const std::string& path);
354 
355 std::string getPathFromSensorNumber(uint16_t sensorNum);
356 
357 namespace ipmi
358 {
359 std::map<std::string, std::vector<std::string>>
360     getObjectInterfaces(const char* path);
361 
362 std::map<std::string, Value> getEntityManagerProperties(const char* path,
363                                                         const char* interface);
364 
365 const std::string* getSensorConfigurationInterface(
366     const std::map<std::string, std::vector<std::string>>&
367         sensorInterfacesResponse);
368 
369 void updateIpmiFromAssociation(const std::string& path,
370                                const DbusInterfaceMap& sensorMap,
371                                uint8_t& entityId, uint8_t& entityInstance);
372 } // namespace ipmi
373