1 #include "IpmbSDRSensor.hpp"
2 
3 #include <sdbusplus/asio/connection.hpp>
4 
5 #include <cmath>
6 #include <cstddef>
7 #include <cstdint>
8 #include <functional>
9 #include <iostream>
10 #include <memory>
11 #include <string>
12 #include <utility>
13 #include <vector>
14 
15 const constexpr char* ipmbService = "xyz.openbmc_project.Ipmi.Channel.Ipmb";
16 const constexpr char* ipmbDbusPath = "/xyz/openbmc_project/Ipmi/Channel/Ipmb";
17 const constexpr char* ipmbInterface = "org.openbmc.Ipmb";
18 const constexpr char* ipmbMethod = "sendRequest";
19 static constexpr uint8_t lun = 0;
20 
IpmbSDRDevice(std::shared_ptr<sdbusplus::asio::connection> & dbusConnection,uint8_t cmdAddr)21 IpmbSDRDevice::IpmbSDRDevice(
22     std::shared_ptr<sdbusplus::asio::connection>& dbusConnection,
23     uint8_t cmdAddr) :
24     commandAddress(cmdAddr << 2),
25     hostIndex(cmdAddr + 1), conn(dbusConnection)
26 {}
27 
validateStatus(boost::system::error_code ec,const IpmbMethodType & response,int hostIndex)28 bool validateStatus(boost::system::error_code ec,
29                     const IpmbMethodType& response, int hostIndex)
30 {
31     if (ec)
32     {
33         return false;
34     }
35 
36     const int status = std::get<0>(response);
37     if (status != 0)
38     {
39         std::cerr << "Error reading from IPMB SDR for host " << hostIndex
40                   << "\n";
41         return false;
42     }
43     return true;
44 }
45 
46 /* This function will store the record count of the SDR sensors for each IPMB
47  * bus */
getSDRRepositoryInfo()48 void IpmbSDRDevice::getSDRRepositoryInfo()
49 {
50     std::weak_ptr<IpmbSDRDevice> weakRef = weak_from_this();
51 
52     conn->async_method_call(
53         [weakRef](boost::system::error_code ec,
54                   const IpmbMethodType& response) {
55         auto self = weakRef.lock();
56         if (!self)
57         {
58             return;
59         }
60 
61         auto status = std::bind_front(validateStatus, ec, response);
62         if (!status(self->hostIndex))
63         {
64             return;
65         }
66 
67         const std::vector<uint8_t>& data = std::get<5>(response);
68         const size_t sdrInfoDataSize = 14;
69 
70         if (data.size() < sdrInfoDataSize)
71         {
72             std::cerr << " IPMB Get SDR Repository Info data is empty for host "
73                       << self->hostIndex << "\n";
74             return;
75         }
76 
77         constexpr uint8_t recordCountLSB = 1;
78         constexpr uint8_t recordCountMSB = 2;
79 
80         uint16_t recordCount = (data[recordCountMSB] << 8) |
81                                data[recordCountLSB];
82 
83         self->reserveSDRRepository(recordCount);
84     },
85         ipmbService, ipmbDbusPath, ipmbInterface, ipmbMethod, commandAddress,
86         sdr::netfnStorageReq, lun, sdr::cmdStorageGetSdrInfo, sdrCommandData);
87 }
88 
89 /* This function will store the reserve ID for each IPMB bus index */
reserveSDRRepository(uint16_t recordCount)90 void IpmbSDRDevice::reserveSDRRepository(uint16_t recordCount)
91 {
92     std::weak_ptr<IpmbSDRDevice> weakRef = weak_from_this();
93 
94     conn->async_method_call(
95         [weakRef, recordCount](boost::system::error_code ec,
96                                const IpmbMethodType& response) {
97         auto self = weakRef.lock();
98         if (!self)
99         {
100             return;
101         }
102 
103         auto status = std::bind_front(validateStatus, ec, response);
104         if (!status(self->hostIndex))
105         {
106             return;
107         }
108 
109         const std::vector<uint8_t>& data = std::get<5>(response);
110         const size_t sdrReserveDataSize = 2;
111 
112         if (data.size() < sdrReserveDataSize)
113         {
114             std::cerr << " IPMB SDR Reserve Repository data is empty for host "
115                       << self->hostIndex << "\n";
116             return;
117         }
118         uint8_t resrvIDLSB = data[0];
119         uint8_t resrvIDMSB = data[1];
120 
121         self->getSDRSensorData(recordCount, resrvIDLSB, resrvIDMSB);
122     },
123         ipmbService, ipmbDbusPath, ipmbInterface, ipmbMethod, commandAddress,
124         sdr::netfnStorageReq, lun, sdr::cmdStorageReserveSdr, sdrCommandData);
125 }
126 
127 /* This function will read all the information related to the sensor
128  * such as name, threshold value, unit, device address, SDR type */
getSDRSensorData(uint16_t recordCount,uint8_t resrvIDLSB,uint8_t resrvIDMSB)129 void IpmbSDRDevice::getSDRSensorData(uint16_t recordCount, uint8_t resrvIDLSB,
130                                      uint8_t resrvIDMSB)
131 {
132     std::weak_ptr<IpmbSDRDevice> weakRef = weak_from_this();
133 
134     uint8_t loopCount = sdr::perCountByte * iCnt;
135     std::vector<uint8_t> commandData = {resrvIDLSB,      resrvIDMSB,
136                                         nextRecordIDLSB, nextRecordIDMSB,
137                                         loopCount,       sdr::perCountByte};
138 
139     conn->async_method_call(
140         [weakRef, recordCount, resrvIDLSB, resrvIDMSB](
141             boost::system::error_code ec, const IpmbMethodType& response) {
142         auto self = weakRef.lock();
143         if (!self)
144         {
145             return;
146         }
147 
148         auto status = std::bind_front(validateStatus, ec, response);
149         if (!status(self->hostIndex))
150         {
151             return;
152         }
153 
154         const std::vector<uint8_t>& data = std::get<5>(response);
155         const size_t sdrSensorDataSize = 18;
156 
157         if (data.size() < sdrSensorDataSize)
158         {
159             std::cerr << "IPMB SDR sensor data is empty for host "
160                       << self->hostIndex << "\n";
161             return;
162         }
163 
164         self->handleSDRData(data, recordCount, resrvIDLSB, resrvIDMSB);
165     },
166         ipmbService, ipmbDbusPath, ipmbInterface, ipmbMethod, commandAddress,
167         sdr::netfnStorageReq, lun, sdr::cmdStorageGetSdr, commandData);
168 }
169 
170 /* This function will handle the sensor data received by IPMB response */
handleSDRData(const std::vector<uint8_t> & data,uint16_t recordCount,uint8_t resrvIDLSB,uint8_t resrvIDMSB)171 void IpmbSDRDevice::handleSDRData(const std::vector<uint8_t>& data,
172                                   uint16_t recordCount, uint8_t resrvIDLSB,
173                                   uint8_t resrvIDMSB)
174 {
175     sdrData.insert(sdrData.end(), data.begin(), data.end());
176 
177     /* dataLength represents the size of data for SDR types */
178     uint8_t dataLength = sdrData[sdr::dataLengthByte] + sdr::dataLengthByte + 1;
179 
180     /*  If sdrData size is less than dataLength, it will call getSDRSensorData
181      *  function recursively till all the data is received.
182      */
183     if (sdrData.size() < dataLength)
184     {
185         iCnt++;
186         getSDRSensorData(recordCount, resrvIDLSB, resrvIDMSB);
187     }
188     else
189     {
190         /*  After all the data is received, it is passed to checkSDRData
191          *  function. Next sensor record ID is stored based on the previous
192          *  record ID. Vector of sdrData is cleared to store next sensor data.
193          *  validRecordCount is incremented and getSDRSensorData function is
194          *  called to proceed with next set of sensors.
195          */
196         checkSDRData(sdrData, dataLength);
197         iCnt = 0;
198         nextRecordIDLSB = sdrData[sdr::sdrNxtRecLSB];
199         nextRecordIDMSB = sdrData[sdr::sdrNxtRecMSB];
200         sdrData.clear();
201 
202         if (validRecordCount == recordCount)
203         {
204             /* Once all the sensors are read and recordCount matched, it will
205              * return. */
206             nextRecordIDLSB = 0;
207             nextRecordIDMSB = 0;
208             return;
209         }
210         validRecordCount++;
211         getSDRSensorData(recordCount, resrvIDLSB, resrvIDMSB);
212     }
213 }
214 
215 /* This function will convert the SDR sensor data such as sensor unit, name, ID,
216  * type from decimal to readable format */
checkSDRData(std::vector<uint8_t> & sdrDataBytes,uint8_t dataLength) const217 void IpmbSDRDevice::checkSDRData(std::vector<uint8_t>& sdrDataBytes,
218                                  uint8_t dataLength) const
219 {
220     if (sdrDataBytes.size() < dataLength)
221     {
222         return;
223     }
224 
225     /* sdrType represents the SDR Type (Byte 5) such as 1, 2, 3 */
226     uint8_t sdrType = sdrDataBytes[sdr::sdrType];
227     if (sdrType != static_cast<uint8_t>(SDRType::sdrType01))
228     {
229         return;
230     }
231 
232     /*  dataLen represents the data length (Byte 6) for SDR sensor */
233     int dataLen = sdrDataBytes[sdr::dataLengthByte];
234 
235     /* iStrLen represents the length of the sensor name for SDR Type 1 */
236     const uint8_t sdrLenBit = 0x1F;
237     int strLen = (sdrDataBytes[sdrtype01::nameLengthByte]) & (sdrLenBit);
238 
239     /* iStrAddr represents the starting byte (Byte 56) for SDR sensor name */
240     int strAddr = dataLen + ((dataLen / (sdr::perCountByte)) * 4) -
241                   (strLen - 1);
242 
243     /* Below for loop will convert the bytes to string and form a sensor name */
244 
245     std::string tempName(sdrDataBytes.begin() + strAddr,
246                          sdrDataBytes.begin() + strAddr + strLen);
247 
248     checkSDRType01Threshold(sdrDataBytes, (hostIndex - 1), tempName);
249 }
250 
251 /* This function will convert the raw value of threshold for each sensor */
checkSDRType01Threshold(std::vector<uint8_t> & sdrDataBytes,int busIndex,std::string tempName)252 void IpmbSDRDevice::checkSDRType01Threshold(std::vector<uint8_t>& sdrDataBytes,
253                                             int busIndex, std::string tempName)
254 {
255     const uint8_t bitShiftMsb = 2;
256     const uint8_t sdrThresAccess = 0x0C;
257 
258     /* linear represents the sensor's linearization (Byte 27) */
259     uint8_t linear = sdrDataBytes[sdrtype01::sdrLinearByte];
260     if (linear != 0)
261     {
262         return;
263     }
264 
265     /* sdrSensCapability (Byte 13) and(&) with sdrThresAccess(0x0C) will declare
266      * whether threshold is present for each sensor */
267     int threshold = (sdrDataBytes[sdrtype01::sensorCapability]) &
268                     (sdrThresAccess);
269 
270     /* mData        - 10 bits
271      * mDataByte    - Byte 28 - 8 bits LSB
272      * mTolDataByte - Byte 29 - 2 bits MSB [7-6]
273      */
274     uint16_t mData = ((sdrDataBytes[sdrtype01::mTolDataByte] & 0xC0)
275                       << bitShiftMsb) |
276                      sdrDataBytes[sdrtype01::mDataByte];
277 
278     /* bData        - 10 bits
279      * bDataByte    - Byte 30 - 8 bits LSB
280      * bAcuDataByte - Byte 31 - 2 bits MSB [7-6]
281      */
282     uint16_t bData = ((sdrDataBytes[sdrtype01::bAcuDataByte] & 0xC0)
283                       << bitShiftMsb) |
284                      sdrDataBytes[sdrtype01::bDataByte];
285 
286     /* rbExpDataByte (Byte 33) represents the exponent value
287      *  Bit [3-0] - B Exponent 2's complement signed bit.
288      *  Bit [7-4] - R Exponent 2's complement signed bit.
289      */
290     int8_t bExpVal = sdrDataBytes[sdrtype01::rbExpDataByte] & 0xF;
291     if (bExpVal > 7)
292     {
293         bExpVal = (~bExpVal + 1) & 0xF;
294     }
295 
296     /* Shifting the data to right by 4, since rExpVal has 4 bits from 4 to 7 in
297      * byte 33 */
298     int8_t rExpVal = (sdrDataBytes[sdrtype01::rbExpDataByte] >> 4) & 0xF;
299     if (rExpVal > 7)
300     {
301         rExpVal = (~rExpVal + 1) & 0xF;
302         rExpVal = -rExpVal;
303     }
304 
305     /* Sensor Threshold Reading Conversion
306      *
307      *  Y = ((Mx + (B * 10^K1)) * (10^K2))
308      *
309      *  X  - Raw value of threshold
310      *  M  - mData Value
311      *  B  - bData Value
312      *  K1 - Signed Exponent of bExpVal
313      *  K2 - Signed Exponent of rExpVal
314      */
315 
316     double bDataVal = bData * pow(10, bExpVal);
317     double expVal = pow(10, rExpVal);
318 
319     double thresUpCri =
320         sensorValCalculation(mData, bDataVal, expVal,
321                              sdrDataBytes[sdrtype01::upperCriticalThreshold]);
322     double thresLoCri =
323         sensorValCalculation(mData, bDataVal, expVal,
324                              sdrDataBytes[sdrtype01::lowerCriticalThreshold]);
325 
326     struct SensorInfo temp;
327 
328     temp.sensorReadName = std::move(tempName);
329     temp.sensorUnit = sdrDataBytes[sdrtype01::sdrUnitType];
330 
331     temp.thresUpperCri = thresUpCri;
332     temp.thresLowerCri = thresLoCri;
333 
334     temp.sensorNumber = sdrDataBytes[sdr::sdrSensorNum];
335     temp.sensCap = threshold;
336 
337     sensorRecord[busIndex].emplace_back(std::move(temp));
338 
339     SensorValConversion val = {mData, bDataVal, expVal,
340                                sdrDataBytes[sdrtype01::sdrNegHandle]};
341 
342     sensorValRecord[busIndex][sdrDataBytes[sdr::sdrSensorNum]] = val;
343 }
344 
345 /* This function will calculate the sensor's threshold value */
sensorValCalculation(uint16_t mValue,double bValue,double expValue,double value)346 double IpmbSDRDevice::sensorValCalculation(uint16_t mValue, double bValue,
347                                            double expValue, double value)
348 {
349     double sensorValue = ((mValue * value) + bValue) * expValue;
350     return sensorValue;
351 }
352