blob: 23d7119b21bcb6090df06070f2cb71fe4b998a7e [file] [log] [blame]
Willy Tude54f482021-01-26 15:59:09 -08001/*
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
Willy Tude54f482021-01-26 15:59:09 -080017#include <boost/bimap.hpp>
18#include <boost/container/flat_map.hpp>
Patrick Williamsfbc6c9d2023-05-10 07:50:16 -050019#include <ipmid/api.hpp>
20#include <ipmid/types.hpp>
21#include <phosphor-logging/log.hpp>
22#include <sdbusplus/bus/match.hpp>
23
Willy Tude54f482021-01-26 15:59:09 -080024#include <cstdio>
25#include <cstring>
26#include <exception>
27#include <filesystem>
Willy Tude54f482021-01-26 15:59:09 -080028#include <map>
Willy Tu4eca2512022-06-20 21:14:51 -070029#include <optional>
Willy Tude54f482021-01-26 15:59:09 -080030#include <string>
Willy Tu4eca2512022-06-20 21:14:51 -070031#include <unordered_set>
Willy Tude54f482021-01-26 15:59:09 -080032#include <vector>
33
34#pragma once
35
36static constexpr bool debug = false;
37
38struct CmpStrVersion
39{
40 bool operator()(std::string a, std::string b) const
41 {
42 return strverscmp(a.c_str(), b.c_str()) < 0;
43 }
44};
45
46using SensorSubTree = boost::container::flat_map<
47 std::string,
48 boost::container::flat_map<std::string, std::vector<std::string>>,
49 CmpStrVersion>;
50
51using SensorNumMap = boost::bimap<int, std::string>;
52
53static constexpr uint16_t maxSensorsPerLUN = 255;
54static constexpr uint16_t maxIPMISensors = (maxSensorsPerLUN * 3);
55static constexpr uint16_t lun1Sensor0 = 0x100;
56static constexpr uint16_t lun3Sensor0 = 0x300;
57static constexpr uint16_t invalidSensorNumber = 0xFFFF;
58static constexpr uint8_t reservedSensorNumber = 0xFF;
59
60namespace details
61{
Josh Lehana55c9532020-10-28 21:59:06 -070062// Enable/disable the logging of stats instrumentation
63static constexpr bool enableInstrumentation = false;
64
65class 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:
77 const std::string& getName(void) const
78 {
79 return sensorName;
80 }
81
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
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 {
Haicheng Zhang041b3752025-07-14 17:13:45 +0800104 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);
Josh Lehana55c9532020-10-28 21:59:06 -0700109 }
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 {
Haicheng Zhang041b3752025-07-14 17:13:45 +0800121 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);
Josh Lehana55c9532020-10-28 21:59:06 -0700127 }
128
129 // Initialize min/max if the first successful reading
130 if (numReadings == 0)
131 {
Haicheng Zhang041b3752025-07-14 17:13:45 +0800132 lg2::error(
133 "IPMI sensor {NAME}: First reading, value={VALUE} byte={BYTE}",
134 "NAME", sensorName, "VALUE", reading, "BYTE", raw);
Josh Lehana55c9532020-10-28 21:59:06 -0700135
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 {
Haicheng Zhang041b3752025-07-14 17:13:45 +0800147 lg2::error(
148 "IPMI sensor {NAME}: Lowest reading, value={VALUE} byte={BYTE}",
149 "NAME", sensorName, "VALUE", reading, "BYTE", raw);
Josh Lehana55c9532020-10-28 21:59:06 -0700150
151 minValue = reading;
152 }
153
154 if (reading > maxValue)
155 {
Haicheng Zhang041b3752025-07-14 17:13:45 +0800156 lg2::error(
157 "IPMI sensor {NAME}: Highest reading, value={VALUE} byte={BYTE}",
158 "NAME", sensorName, "VALUE", reading, "BYTE", raw);
Josh Lehana55c9532020-10-28 21:59:06 -0700159
160 maxValue = reading;
161 }
162
163 return first;
164 }
165};
166
167class IPMIStatsTable
168{
169 private:
170 std::vector<IPMIStatsEntry> entries;
171
172 private:
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:
190 void wipeTable(void)
191 {
192 entries.clear();
193 }
194
195 const std::string& getName(size_t index)
196 {
197 padEntries(index);
198 return entries[index].getName();
199 }
200
201 void updateName(size_t index, std::string_view name)
202 {
203 padEntries(index);
204 entries[index].updateName(name);
205 }
206
207 bool updateReading(size_t index, double reading, int raw)
208 {
209 padEntries(index);
210 return entries[index].updateReading(reading, raw);
211 }
212};
213
Jie Yangf0a89942021-07-29 15:30:25 -0700214class IPMIWriteEntry
215{
216 private:
217 bool writePermission = false;
218
219 public:
220 bool getWritePermission(void) const
221 {
222 return writePermission;
223 }
224
225 void setWritePermission(bool permission)
226 {
227 writePermission = permission;
228 }
229};
230
231class IPMIWriteTable
232{
233 private:
234 std::vector<IPMIWriteEntry> entries;
235
236 private:
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:
247 void wipeTable(void)
248 {
249 entries.clear();
250 }
251
252 bool getWritePermission(size_t index)
253 {
254 padEntries(index);
255 return entries[index].getWritePermission();
256 }
257
258 void setWritePermission(size_t index, bool permission)
259 {
260 padEntries(index);
261 entries[index].setWritePermission(permission);
262 }
263};
264
Hao Jiangd2afd052020-12-10 15:09:32 -0800265// 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.
Josh Lehana55c9532020-10-28 21:59:06 -0700267inline IPMIStatsTable sdrStatsTable;
Jie Yangf0a89942021-07-29 15:30:25 -0700268inline IPMIWriteTable sdrWriteTable;
Josh Lehana55c9532020-10-28 21:59:06 -0700269
Hao Jiang9a5b51e2021-01-06 10:32:22 -0800270/**
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 */
Kuiying Wanga8b5b262021-02-06 23:38:22 +0800281uint16_t getSensorSubtree(std::shared_ptr<SensorSubTree>& subtree);
Willy Tude54f482021-01-26 15:59:09 -0800282
283bool getSensorNumMap(std::shared_ptr<SensorNumMap>& sensorNumMap);
284} // namespace details
285
286bool getSensorSubtree(SensorSubTree& subtree);
287
Scron Chang2703b022021-07-06 15:47:45 +0800288#ifdef FEATURE_HYBRID_SENSORS
Patrick Williams69b4c282025-03-03 11:19:13 -0500289ipmi::sensor::IdInfoMap::const_iterator findStaticSensor(
290 const std::string& path);
Scron Chang2703b022021-07-06 15:47:45 +0800291#endif
292
Willy Tude54f482021-01-26 15:59:09 -0800293struct CmpStr
294{
295 bool operator()(const char* a, const char* b) const
296 {
297 return std::strcmp(a, b) < 0;
298 }
299};
300
Scron Chang2b42d7e2021-07-06 15:45:47 +0800301static constexpr size_t sensorTypeCodes = 0;
302static constexpr size_t sensorEventTypeCodes = 1;
303
Willy Tude54f482021-01-26 15:59:09 -0800304enum class SensorTypeCodes : uint8_t
305{
David Wang604e0c62021-11-17 11:06:54 +0800306 reserved = 0x00,
307 temperature = 0x01,
308 voltage = 0x02,
309 current = 0x03,
310 fan = 0x04,
George Liuf2807ed2025-04-03 15:52:57 +0800311 physicalSecurity = 0x5,
David Wang604e0c62021-11-17 11:06:54 +0800312 processor = 0x07,
George Liuf2807ed2025-04-03 15:52:57 +0800313 powerUnit = 0x09,
David Wang604e0c62021-11-17 11:06:54 +0800314 other = 0x0b,
315 memory = 0x0c,
Scron Changb8e5b162021-07-06 15:46:43 +0800316 buttons = 0x14,
317 watchdog2 = 0x23,
Duke Du363d8d52022-04-28 14:56:42 +0800318 entity = 0x25,
Johnathan Mantey34d19732024-07-17 07:59:54 -0700319 oemC0 = 0xc0,
Willy Tude54f482021-01-26 15:59:09 -0800320};
321
Scron Chang2b42d7e2021-07-06 15:45:47 +0800322enum class SensorEventTypeCodes : uint8_t
323{
324 unspecified = 0x00,
325 threshold = 0x01,
326 sensorSpecified = 0x6f
327};
328
Johnathan Mantey34d19732024-07-17 07:59:54 -0700329extern boost::container::flat_map<
Scron Chang2b42d7e2021-07-06 15:45:47 +0800330 const char*, std::pair<SensorTypeCodes, SensorEventTypeCodes>, CmpStr>
Johnathan Mantey34d19732024-07-17 07:59:54 -0700331 sensorTypes;
332
Willy Tude54f482021-01-26 15:59:09 -0800333std::string getSensorTypeStringFromPath(const std::string& path);
334
335uint8_t getSensorTypeFromPath(const std::string& path);
336
337uint16_t getSensorNumberFromPath(const std::string& path);
338
339uint8_t getSensorEventTypeFromPath(const std::string& path);
340
341std::string getPathFromSensorNumber(uint16_t sensorNum);
342
343namespace ipmi
344{
Alexander Hansen8fb5b892023-11-02 17:29:34 +0100345std::optional<std::map<std::string, std::vector<std::string>>>
Willy Tude54f482021-01-26 15:59:09 -0800346 getObjectInterfaces(const char* path);
347
Patrick Williams69b4c282025-03-03 11:19:13 -0500348std::map<std::string, Value> getEntityManagerProperties(const char* path,
349 const char* interface);
Willy Tude54f482021-01-26 15:59:09 -0800350
Patrick Williams69b4c282025-03-03 11:19:13 -0500351std::optional<std::unordered_set<std::string>>& getIpmiDecoratorPaths(
352 const std::optional<ipmi::Context::ptr>& ctx);
Willy Tu4eca2512022-06-20 21:14:51 -0700353
Willy Tude54f482021-01-26 15:59:09 -0800354const std::string* getSensorConfigurationInterface(
355 const std::map<std::string, std::vector<std::string>>&
356 sensorInterfacesResponse);
357
Willy Tu4eca2512022-06-20 21:14:51 -0700358void 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);
Willy Tude54f482021-01-26 15:59:09 -0800363} // namespace ipmi