xref: /openbmc/phosphor-host-ipmid/include/dbus-sdr/sdrutils.hpp (revision 9a5b51e3dea5d52861ce7b2c18ca826b7b2bca4c)
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 // 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.
214 inline IPMIStatsTable sdrStatsTable;
215 
216 /**
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  */
227 uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree);
228 
229 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap);
230 } // namespace details
231 
232 bool getSensorSubtree(SensorSubTree& subtree);
233 
234 struct CmpStr
235 {
236     bool operator()(const char* a, const char* b) const
237     {
238         return std::strcmp(a, b) < 0;
239     }
240 };
241 
242 enum 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 
252 const 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 
260 std::string getSensorTypeStringFromPath(const std::string& path);
261 
262 uint8_t getSensorTypeFromPath(const std::string& path);
263 
264 uint16_t getSensorNumberFromPath(const std::string& path);
265 
266 uint8_t getSensorEventTypeFromPath(const std::string& path);
267 
268 std::string getPathFromSensorNumber(uint16_t sensorNum);
269 
270 namespace ipmi
271 {
272 std::map<std::string, std::vector<std::string>>
273     getObjectInterfaces(const char* path);
274 
275 std::map<std::string, Value> getEntityManagerProperties(const char* path,
276                                                         const char* interface);
277 
278 const std::string* getSensorConfigurationInterface(
279     const std::map<std::string, std::vector<std::string>>&
280         sensorInterfacesResponse);
281 
282 void updateIpmiFromAssociation(const std::string& path,
283                                const DbusInterfaceMap& sensorMap,
284                                uint8_t& entityId, uint8_t& entityInstance);
285 } // namespace ipmi
286