xref: /openbmc/phosphor-host-ipmid/include/dbus-sdr/sdrutils.hpp (revision c024b39d16e3c23c2b200b01e9aa8b5252498f56)
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/bimap.hpp>
18 #include <boost/container/flat_map.hpp>
19 #include <ipmid/api.hpp>
20 #include <ipmid/types.hpp>
21 #include <phosphor-logging/log.hpp>
22 #include <sdbusplus/bus/match.hpp>
23 
24 #include <cstdio>
25 #include <cstring>
26 #include <exception>
27 #include <filesystem>
28 #include <map>
29 #include <optional>
30 #include <string>
31 #include <unordered_set>
32 #include <vector>
33 
34 #pragma once
35 
36 static constexpr bool debug = false;
37 
38 struct CmpStrVersion
39 {
operator ()CmpStrVersion40     bool operator()(std::string a, std::string b) const
41     {
42         return strverscmp(a.c_str(), b.c_str()) < 0;
43     }
44 };
45 
46 using SensorSubTree = boost::container::flat_map<
47     std::string,
48     boost::container::flat_map<std::string, std::vector<std::string>>,
49     CmpStrVersion>;
50 
51 using SensorNumMap = boost::bimap<int, std::string>;
52 
53 static constexpr uint16_t maxSensorsPerLUN = 255;
54 static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3);
55 static constexpr uint16_t lun1Sensor0 = 0x100;
56 static constexpr uint16_t lun3Sensor0 = 0x300;
57 static constexpr uint16_t invalidSensorNumber = 0xFFFF;
58 static constexpr uint8_t reservedSensorNumber = 0xFF;
59 
60 namespace details
61 {
62 // Enable/disable the logging of stats instrumentation
63 static constexpr bool enableInstrumentation = false;
64 
65 class IPMIStatsEntry
66 {
67   private:
68     int numReadings = 0;
69     int numMissings = 0;
70     int numStreakRead = 0;
71     int numStreakMiss = 0;
72     double minValue = 0.0;
73     double maxValue = 0.0;
74     std::string sensorName;
75 
76   public:
getName(void) const77     const std::string& getName(void) const
78     {
79         return sensorName;
80     }
81 
updateName(std::string_view name)82     void updateName(std::string_view name)
83     {
84         sensorName = name;
85     }
86 
87     // Returns true if this is the first successful reading
88     // This is so the caller can log the coefficients used
updateReading(double reading,int raw)89     bool updateReading(double reading, int raw)
90     {
91         if constexpr (!enableInstrumentation)
92         {
93             return false;
94         }
95 
96         bool first = ((numReadings == 0) && (numMissings == 0));
97 
98         // Sensors can use "nan" to indicate unavailable reading
99         if (!(std::isfinite(reading)))
100         {
101             // Only show this if beginning a new streak
102             if (numStreakMiss == 0)
103             {
104                 lg2::error(
105                     "IPMI sensor {NAME}: Missing reading, byte={BYTE}, Reading "
106                     "counts good={GOOD} miss={MISS}, Prior good streak={STREAK}",
107                     "NAME", sensorName, "BYTE", raw, "GOOD", numReadings,
108                     "MISS", numMissings, "STREAK", numStreakRead);
109             }
110 
111             numStreakRead = 0;
112             ++numMissings;
113             ++numStreakMiss;
114 
115             return first;
116         }
117 
118         // Only show this if beginning a new streak and not the first time
119         if ((numStreakRead == 0) && (numReadings != 0))
120         {
121             lg2::error(
122                 "IPMI sensor {NAME}: Recovered reading, value={VALUE} byte={BYTE}"
123                 ", Reading counts good={GOOD} miss={MISS}, Prior miss "
124                 "streak={STREAK}",
125                 "NAME", sensorName, "VALUE", reading, "BYTE", raw, "GOOD",
126                 numReadings, "MISS", numMissings, "STREAK", numStreakMiss);
127         }
128 
129         // Initialize min/max if the first successful reading
130         if (numReadings == 0)
131         {
132             lg2::error(
133                 "IPMI sensor {NAME}: First reading, value={VALUE} byte={BYTE}",
134                 "NAME", sensorName, "VALUE", reading, "BYTE", raw);
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             lg2::error(
148                 "IPMI sensor {NAME}: Lowest reading, value={VALUE} byte={BYTE}",
149                 "NAME", sensorName, "VALUE", reading, "BYTE", raw);
150 
151             minValue = reading;
152         }
153 
154         if (reading > maxValue)
155         {
156             lg2::error(
157                 "IPMI sensor {NAME}: Highest reading, value={VALUE} byte={BYTE}",
158                 "NAME", sensorName, "VALUE", reading, "BYTE", raw);
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 findStaticSensor(
290     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     physicalSecurity = 0x5,
312     processor = 0x07,
313     powerUnit = 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> getEntityManagerProperties(const char* path,
349                                                         const char* interface);
350 
351 std::optional<std::unordered_set<std::string>>& getIpmiDecoratorPaths(
352     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