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 <ipmid/api.hpp>
21 #include <ipmid/types.hpp>
22 #include <phosphor-logging/log.hpp>
23 #include <sdbusplus/bus/match.hpp>
24 
25 #include <cstdio>
26 #include <cstring>
27 #include <exception>
28 #include <filesystem>
29 #include <map>
30 #include <optional>
31 #include <string>
32 #include <unordered_set>
33 #include <vector>
34 
35 #pragma once
36 
37 static constexpr bool debug = false;
38 
39 struct CmpStrVersion
40 {
41     bool operator()(std::string a, std::string b) const
42     {
43         return strverscmp(a.c_str(), b.c_str()) < 0;
44     }
45 };
46 
47 using SensorSubTree = boost::container::flat_map<
48     std::string,
49     boost::container::flat_map<std::string, std::vector<std::string>>,
50     CmpStrVersion>;
51 
52 using SensorNumMap = boost::bimap<int, std::string>;
53 
54 static constexpr uint16_t maxSensorsPerLUN = 255;
55 static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3);
56 static constexpr uint16_t lun1Sensor0 = 0x100;
57 static constexpr uint16_t lun3Sensor0 = 0x300;
58 static constexpr uint16_t invalidSensorNumber = 0xFFFF;
59 static constexpr uint8_t reservedSensorNumber = 0xFF;
60 
61 namespace details
62 {
63 // Enable/disable the logging of stats instrumentation
64 static constexpr bool enableInstrumentation = false;
65 
66 class IPMIStatsEntry
67 {
68   private:
69     int numReadings = 0;
70     int numMissings = 0;
71     int numStreakRead = 0;
72     int numStreakMiss = 0;
73     double minValue = 0.0;
74     double maxValue = 0.0;
75     std::string sensorName;
76 
77   public:
78     const std::string& getName(void) const
79     {
80         return sensorName;
81     }
82 
83     void updateName(std::string_view name)
84     {
85         sensorName = name;
86     }
87 
88     // Returns true if this is the first successful reading
89     // This is so the caller can log the coefficients used
90     bool updateReading(double reading, int raw)
91     {
92         if constexpr (!enableInstrumentation)
93         {
94             return false;
95         }
96 
97         bool first = ((numReadings == 0) && (numMissings == 0));
98 
99         // Sensors can use "nan" to indicate unavailable reading
100         if (!(std::isfinite(reading)))
101         {
102             // Only show this if beginning a new streak
103             if (numStreakMiss == 0)
104             {
105                 std::cerr << "IPMI sensor " << sensorName
106                           << ": Missing reading, byte=" << raw
107                           << ", Reading counts good=" << numReadings
108                           << " miss=" << numMissings
109                           << ", Prior good streak=" << numStreakRead << "\n";
110             }
111 
112             numStreakRead = 0;
113             ++numMissings;
114             ++numStreakMiss;
115 
116             return first;
117         }
118 
119         // Only show this if beginning a new streak and not the first time
120         if ((numStreakRead == 0) && (numReadings != 0))
121         {
122             std::cerr << "IPMI sensor " << sensorName
123                       << ": Recovered reading, value=" << reading
124                       << " byte=" << raw
125                       << ", Reading counts good=" << numReadings
126                       << " miss=" << numMissings
127                       << ", Prior miss streak=" << numStreakMiss << "\n";
128         }
129 
130         // Initialize min/max if the first successful reading
131         if (numReadings == 0)
132         {
133             std::cerr << "IPMI sensor " << sensorName
134                       << ": First reading, value=" << reading << " byte=" << raw
135                       << "\n";
136 
137             minValue = reading;
138             maxValue = reading;
139         }
140 
141         numStreakMiss = 0;
142         ++numReadings;
143         ++numStreakRead;
144 
145         // Only provide subsequent output if new min/max established
146         if (reading < minValue)
147         {
148             std::cerr << "IPMI sensor " << sensorName
149                       << ": Lowest reading, value=" << reading
150                       << " byte=" << raw << "\n";
151 
152             minValue = reading;
153         }
154 
155         if (reading > maxValue)
156         {
157             std::cerr << "IPMI sensor " << sensorName
158                       << ": Highest reading, value=" << reading
159                       << " byte=" << raw << "\n";
160 
161             maxValue = reading;
162         }
163 
164         return first;
165     }
166 };
167 
168 class IPMIStatsTable
169 {
170   private:
171     std::vector<IPMIStatsEntry> entries;
172 
173   private:
174     void padEntries(size_t index)
175     {
176         char hexbuf[16];
177 
178         // Pad vector until entries[index] becomes a valid index
179         while (entries.size() <= index)
180         {
181             // As name not known yet, use human-readable hex as name
182             IPMIStatsEntry newEntry;
183             sprintf(hexbuf, "0x%02zX", entries.size());
184             newEntry.updateName(hexbuf);
185 
186             entries.push_back(std::move(newEntry));
187         }
188     }
189 
190   public:
191     void wipeTable(void)
192     {
193         entries.clear();
194     }
195 
196     const std::string& getName(size_t index)
197     {
198         padEntries(index);
199         return entries[index].getName();
200     }
201 
202     void updateName(size_t index, std::string_view name)
203     {
204         padEntries(index);
205         entries[index].updateName(name);
206     }
207 
208     bool updateReading(size_t index, double reading, int raw)
209     {
210         padEntries(index);
211         return entries[index].updateReading(reading, raw);
212     }
213 };
214 
215 class IPMIWriteEntry
216 {
217   private:
218     bool writePermission = false;
219 
220   public:
221     bool getWritePermission(void) const
222     {
223         return writePermission;
224     }
225 
226     void setWritePermission(bool permission)
227     {
228         writePermission = permission;
229     }
230 };
231 
232 class IPMIWriteTable
233 {
234   private:
235     std::vector<IPMIWriteEntry> entries;
236 
237   private:
238     void padEntries(size_t index)
239     {
240         // Pad vector until entries[index] becomes a valid index
241         if (entries.size() <= index)
242         {
243             entries.resize(index + 1);
244         }
245     }
246 
247   public:
248     void wipeTable(void)
249     {
250         entries.clear();
251     }
252 
253     bool getWritePermission(size_t index)
254     {
255         padEntries(index);
256         return entries[index].getWritePermission();
257     }
258 
259     void setWritePermission(size_t index, bool permission)
260     {
261         padEntries(index);
262         entries[index].setWritePermission(permission);
263     }
264 };
265 
266 // Store information for threshold sensors and they are not used by VR
267 // sensors. These objects are global singletons, used from a variety of places.
268 inline IPMIStatsTable sdrStatsTable;
269 inline IPMIWriteTable sdrWriteTable;
270 
271 /**
272  * Search ObjectMapper for sensors and update them to subtree.
273  *
274  * The function will search for sensors under either
275  * /xyz/openbmc_project/sensors or /xyz/openbmc_project/extsensors. It will
276  * optionally search VR typed sensors under /xyz/openbmc_project/vr
277  *
278  * @return the updated amount of times any of "sensors" or "extsensors" sensor
279  * paths updated successfully, previous amount if all failed. The "vr"
280  * sensor path is optional, and does not participate in the return value.
281  */
282 uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree);
283 
284 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap);
285 } // namespace details
286 
287 bool getSensorSubtree(SensorSubTree& subtree);
288 
289 #ifdef FEATURE_HYBRID_SENSORS
290 ipmi::sensor::IdInfoMap::const_iterator
291     findStaticSensor(const std::string& path);
292 #endif
293 
294 struct CmpStr
295 {
296     bool operator()(const char* a, const char* b) const
297     {
298         return std::strcmp(a, b) < 0;
299     }
300 };
301 
302 static constexpr size_t sensorTypeCodes = 0;
303 static constexpr size_t sensorEventTypeCodes = 1;
304 
305 enum class SensorTypeCodes : uint8_t
306 {
307     reserved = 0x00,
308     temperature = 0x01,
309     voltage = 0x02,
310     current = 0x03,
311     fan = 0x04,
312     physical_security = 0x5,
313     processor = 0x07,
314     power_unit = 0x09,
315     other = 0x0b,
316     memory = 0x0c,
317     buttons = 0x14,
318     watchdog2 = 0x23,
319     entity = 0x25,
320     oemC0 = 0xc0,
321 };
322 
323 enum class SensorEventTypeCodes : uint8_t
324 {
325     unspecified = 0x00,
326     threshold = 0x01,
327     sensorSpecified = 0x6f
328 };
329 
330 extern boost::container::flat_map<
331     const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr>
332     sensorTypes;
333 
334 std::string getSensorTypeStringFromPath(const std::string& path);
335 
336 uint8_t getSensorTypeFromPath(const std::string& path);
337 
338 uint16_t getSensorNumberFromPath(const std::string& path);
339 
340 uint8_t getSensorEventTypeFromPath(const std::string& path);
341 
342 std::string getPathFromSensorNumber(uint16_t sensorNum);
343 
344 namespace ipmi
345 {
346 std::optional<std::map<std::string, std::vector<std::string>>>
347     getObjectInterfaces(const char* path);
348 
349 std::map<std::string, Value> getEntityManagerProperties(const char* path,
350                                                         const char* interface);
351 
352 std::optional<std::unordered_set<std::string>>&
353     getIpmiDecoratorPaths(const std::optional<ipmi::Context::ptr>& ctx);
354 
355 const std::string* getSensorConfigurationInterface(
356     const std::map<std::string, std::vector<std::string>>&
357         sensorInterfacesResponse);
358 
359 void updateIpmiFromAssociation(
360     const std::string& path,
361     const std::unordered_set<std::string>& ipmiDecoratorPaths,
362     const DbusInterfaceMap& sensorMap, uint8_t& entityId,
363     uint8_t& entityInstance);
364 } // namespace ipmi
365