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 #ifdef FEATURE_HYBRID_SENSORS
235 ipmi::sensor::IdInfoMap::const_iterator
236     findStaticSensor(const std::string& path);
237 #endif
238 
239 struct CmpStr
240 {
241     bool operator()(const char* a, const char* b) const
242     {
243         return std::strcmp(a, b) < 0;
244     }
245 };
246 
247 static constexpr size_t sensorTypeCodes = 0;
248 static constexpr size_t sensorEventTypeCodes = 1;
249 
250 enum class SensorTypeCodes : uint8_t
251 {
252     reserved = 0x0,
253     temperature = 0x1,
254     voltage = 0x2,
255     current = 0x3,
256     fan = 0x4,
257     other = 0xB,
258     memory = 0x0c,
259     power_unit = 0x09,
260     buttons = 0x14,
261     watchdog2 = 0x23,
262 };
263 
264 enum class SensorEventTypeCodes : uint8_t
265 {
266     unspecified = 0x00,
267     threshold = 0x01,
268     sensorSpecified = 0x6f
269 };
270 
271 const 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,
285                                   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)}}};
294 
295 std::string getSensorTypeStringFromPath(const std::string& path);
296 
297 uint8_t getSensorTypeFromPath(const std::string& path);
298 
299 uint16_t getSensorNumberFromPath(const std::string& path);
300 
301 uint8_t getSensorEventTypeFromPath(const std::string& path);
302 
303 std::string getPathFromSensorNumber(uint16_t sensorNum);
304 
305 namespace ipmi
306 {
307 std::map<std::string, std::vector<std::string>>
308     getObjectInterfaces(const char* path);
309 
310 std::map<std::string, Value> getEntityManagerProperties(const char* path,
311                                                         const char* interface);
312 
313 const std::string* getSensorConfigurationInterface(
314     const std::map<std::string, std::vector<std::string>>&
315         sensorInterfacesResponse);
316 
317 void updateIpmiFromAssociation(const std::string& path,
318                                const DbusInterfaceMap& sensorMap,
319                                uint8_t& entityId, uint8_t& entityInstance);
320 } // namespace ipmi
321