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 {
operator ()CmpStrVersion41     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:
getName(void) const78     const std::string& getName(void) const
79     {
80         return sensorName;
81     }
82 
updateName(std::string_view name)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
updateReading(double reading,int raw)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 << " byte="
124                       << raw << ", Reading counts good=" << numReadings
125                       << " miss=" << numMissings
126                       << ", Prior miss streak=" << numStreakMiss << "\n";
127         }
128 
129         // Initialize min/max if the first successful reading
130         if (numReadings == 0)
131         {
132             std::cerr << "IPMI sensor " << sensorName
133                       << ": First reading, value=" << reading << " byte=" << raw
134                       << "\n";
135 
136             minValue = reading;
137             maxValue = reading;
138         }
139 
140         numStreakMiss = 0;
141         ++numReadings;
142         ++numStreakRead;
143 
144         // Only provide subsequent output if new min/max established
145         if (reading < minValue)
146         {
147             std::cerr << "IPMI sensor " << sensorName
148                       << ": Lowest reading, value=" << reading
149                       << " byte=" << raw << "\n";
150 
151             minValue = reading;
152         }
153 
154         if (reading > maxValue)
155         {
156             std::cerr << "IPMI sensor " << sensorName
157                       << ": Highest reading, value=" << reading
158                       << " byte=" << raw << "\n";
159 
160             maxValue = reading;
161         }
162 
163         return first;
164     }
165 };
166 
167 class IPMIStatsTable
168 {
169   private:
170     std::vector<IPMIStatsEntry> entries;
171 
172   private:
padEntries(size_t index)173     void padEntries(size_t index)
174     {
175         char hexbuf[16];
176 
177         // Pad vector until entries[index] becomes a valid index
178         while (entries.size() <= index)
179         {
180             // As name not known yet, use human-readable hex as name
181             IPMIStatsEntry newEntry;
182             sprintf(hexbuf, "0x%02zX", entries.size());
183             newEntry.updateName(hexbuf);
184 
185             entries.push_back(std::move(newEntry));
186         }
187     }
188 
189   public:
wipeTable(void)190     void wipeTable(void)
191     {
192         entries.clear();
193     }
194 
getName(size_t index)195     const std::string& getName(size_t index)
196     {
197         padEntries(index);
198         return entries[index].getName();
199     }
200 
updateName(size_t index,std::string_view name)201     void updateName(size_t index, std::string_view name)
202     {
203         padEntries(index);
204         entries[index].updateName(name);
205     }
206 
updateReading(size_t index,double reading,int raw)207     bool updateReading(size_t index, double reading, int raw)
208     {
209         padEntries(index);
210         return entries[index].updateReading(reading, raw);
211     }
212 };
213 
214 class IPMIWriteEntry
215 {
216   private:
217     bool writePermission = false;
218 
219   public:
getWritePermission(void) const220     bool getWritePermission(void) const
221     {
222         return writePermission;
223     }
224 
setWritePermission(bool permission)225     void setWritePermission(bool permission)
226     {
227         writePermission = permission;
228     }
229 };
230 
231 class IPMIWriteTable
232 {
233   private:
234     std::vector<IPMIWriteEntry> entries;
235 
236   private:
padEntries(size_t index)237     void padEntries(size_t index)
238     {
239         // Pad vector until entries[index] becomes a valid index
240         if (entries.size() <= index)
241         {
242             entries.resize(index + 1);
243         }
244     }
245 
246   public:
wipeTable(void)247     void wipeTable(void)
248     {
249         entries.clear();
250     }
251 
getWritePermission(size_t index)252     bool getWritePermission(size_t index)
253     {
254         padEntries(index);
255         return entries[index].getWritePermission();
256     }
257 
setWritePermission(size_t index,bool permission)258     void setWritePermission(size_t index, bool permission)
259     {
260         padEntries(index);
261         entries[index].setWritePermission(permission);
262     }
263 };
264 
265 // Store information for threshold sensors and they are not used by VR
266 // sensors. These objects are global singletons, used from a variety of places.
267 inline IPMIStatsTable sdrStatsTable;
268 inline IPMIWriteTable sdrWriteTable;
269 
270 /**
271  * Search ObjectMapper for sensors and update them to subtree.
272  *
273  * The function will search for sensors under either
274  * /xyz/openbmc_project/sensors or /xyz/openbmc_project/extsensors. It will
275  * optionally search VR typed sensors under /xyz/openbmc_project/vr
276  *
277  * @return the updated amount of times any of "sensors" or "extsensors" sensor
278  * paths updated successfully, previous amount if all failed. The "vr"
279  * sensor path is optional, and does not participate in the return value.
280  */
281 uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree);
282 
283 bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap);
284 } // namespace details
285 
286 bool getSensorSubtree(SensorSubTree& subtree);
287 
288 #ifdef FEATURE_HYBRID_SENSORS
289 ipmi::sensor::IdInfoMap::const_iterator
290     findStaticSensor(const std::string& path);
291 #endif
292 
293 struct CmpStr
294 {
operator ()CmpStr295     bool operator()(const char* a, const char* b) const
296     {
297         return std::strcmp(a, b) < 0;
298     }
299 };
300 
301 static constexpr size_t sensorTypeCodes = 0;
302 static constexpr size_t sensorEventTypeCodes = 1;
303 
304 enum class SensorTypeCodes : uint8_t
305 {
306     reserved = 0x00,
307     temperature = 0x01,
308     voltage = 0x02,
309     current = 0x03,
310     fan = 0x04,
311     physical_security = 0x5,
312     processor = 0x07,
313     power_unit = 0x09,
314     other = 0x0b,
315     memory = 0x0c,
316     buttons = 0x14,
317     watchdog2 = 0x23,
318     entity = 0x25,
319     oemC0 = 0xc0,
320 };
321 
322 enum class SensorEventTypeCodes : uint8_t
323 {
324     unspecified = 0x00,
325     threshold = 0x01,
326     sensorSpecified = 0x6f
327 };
328 
329 extern boost::container::flat_map<
330     const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr>
331     sensorTypes;
332 
333 std::string getSensorTypeStringFromPath(const std::string& path);
334 
335 uint8_t getSensorTypeFromPath(const std::string& path);
336 
337 uint16_t getSensorNumberFromPath(const std::string& path);
338 
339 uint8_t getSensorEventTypeFromPath(const std::string& path);
340 
341 std::string getPathFromSensorNumber(uint16_t sensorNum);
342 
343 namespace ipmi
344 {
345 std::optional<std::map<std::string, std::vector<std::string>>>
346     getObjectInterfaces(const char* path);
347 
348 std::map<std::string, Value>
349     getEntityManagerProperties(const char* path, const char* interface);
350 
351 std::optional<std::unordered_set<std::string>>&
352     getIpmiDecoratorPaths(const std::optional<ipmi::Context::ptr>& ctx);
353 
354 const std::string* getSensorConfigurationInterface(
355     const std::map<std::string, std::vector<std::string>>&
356         sensorInterfacesResponse);
357 
358 void updateIpmiFromAssociation(
359     const std::string& path,
360     const std::unordered_set<std::string>& ipmiDecoratorPaths,
361     const DbusInterfaceMap& sensorMap, uint8_t& entityId,
362     uint8_t& entityInstance);
363 } // namespace ipmi
364