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