1 #include "config.h"
2 
3 #include "sensorhandler.hpp"
4 
5 #include "fruread.hpp"
6 
7 #include <mapper.h>
8 #include <systemd/sd-bus.h>
9 
10 #include <bitset>
11 #include <cmath>
12 #include <cstring>
13 #include <ipmid/api.hpp>
14 #include <ipmid/types.hpp>
15 #include <ipmid/utils.hpp>
16 #include <phosphor-logging/elog-errors.hpp>
17 #include <phosphor-logging/log.hpp>
18 #include <sdbusplus/message/types.hpp>
19 #include <set>
20 #include <xyz/openbmc_project/Common/error.hpp>
21 #include <xyz/openbmc_project/Sensor/Value/server.hpp>
22 
23 static constexpr uint8_t fruInventoryDevice = 0x10;
24 static constexpr uint8_t IPMIFruInventory = 0x02;
25 static constexpr uint8_t BMCSlaveAddress = 0x20;
26 
27 extern int updateSensorRecordFromSSRAESC(const void*);
28 extern sd_bus* bus;
29 extern const ipmi::sensor::IdInfoMap sensors;
30 extern const FruMap frus;
31 extern const ipmi::sensor::EntityInfoMap entities;
32 
33 using namespace phosphor::logging;
34 using InternalFailure =
35     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
36 
37 void register_netfn_sen_functions() __attribute__((constructor));
38 
39 struct sensorTypemap_t
40 {
41     uint8_t number;
42     uint8_t typecode;
43     char dbusname[32];
44 };
45 
46 sensorTypemap_t g_SensorTypeMap[] = {
47 
48     {0x01, 0x6F, "Temp"},
49     {0x0C, 0x6F, "DIMM"},
50     {0x0C, 0x6F, "MEMORY_BUFFER"},
51     {0x07, 0x6F, "PROC"},
52     {0x07, 0x6F, "CORE"},
53     {0x07, 0x6F, "CPU"},
54     {0x0F, 0x6F, "BootProgress"},
55     {0xe9, 0x09, "OccStatus"}, // E9 is an internal mapping to handle sensor
56                                // type code os 0x09
57     {0xC3, 0x6F, "BootCount"},
58     {0x1F, 0x6F, "OperatingSystemStatus"},
59     {0x12, 0x6F, "SYSTEM_EVENT"},
60     {0xC7, 0x03, "SYSTEM"},
61     {0xC7, 0x03, "MAIN_PLANAR"},
62     {0xC2, 0x6F, "PowerCap"},
63     {0x0b, 0xCA, "PowerSupplyRedundancy"},
64     {0xDA, 0x03, "TurboAllowed"},
65     {0xD8, 0xC8, "PowerSupplyDerating"},
66     {0xFF, 0x00, ""},
67 };
68 
69 struct sensor_data_t
70 {
71     uint8_t sennum;
72 } __attribute__((packed));
73 
74 struct sensorreadingresp_t
75 {
76     uint8_t value;
77     uint8_t operation;
78     uint8_t indication[2];
79 } __attribute__((packed));
80 
81 const ipmi::sensor::EntityInfoMap& getIpmiEntityRecords()
82 {
83     return entities;
84 }
85 
86 int get_bus_for_path(const char* path, char** busname)
87 {
88     return mapper_get_service(bus, path, busname);
89 }
90 
91 // Use a lookup table to find the interface name of a specific sensor
92 // This will be used until an alternative is found.  this is the first
93 // step for mapping IPMI
94 int find_openbmc_path(uint8_t num, dbus_interface_t* interface)
95 {
96     int rc;
97 
98     const auto& sensor_it = sensors.find(num);
99     if (sensor_it == sensors.end())
100     {
101         // The sensor map does not contain the sensor requested
102         return -EINVAL;
103     }
104 
105     const auto& info = sensor_it->second;
106 
107     char* busname = nullptr;
108     rc = get_bus_for_path(info.sensorPath.c_str(), &busname);
109     if (rc < 0)
110     {
111         std::fprintf(stderr, "Failed to get %s busname: %s\n",
112                      info.sensorPath.c_str(), busname);
113         goto final;
114     }
115 
116     interface->sensortype = info.sensorType;
117     strcpy(interface->bus, busname);
118     strcpy(interface->path, info.sensorPath.c_str());
119     // Take the interface name from the beginning of the DbusInterfaceMap. This
120     // works for the Value interface but may not suffice for more complex
121     // sensors.
122     // tracked https://github.com/openbmc/phosphor-host-ipmid/issues/103
123     strcpy(interface->interface,
124            info.propertyInterfaces.begin()->first.c_str());
125     interface->sensornumber = num;
126 
127 final:
128     free(busname);
129     return rc;
130 }
131 
132 /////////////////////////////////////////////////////////////////////
133 //
134 // Routines used by ipmi commands wanting to interact on the dbus
135 //
136 /////////////////////////////////////////////////////////////////////
137 int set_sensor_dbus_state_s(uint8_t number, const char* method,
138                             const char* value)
139 {
140 
141     dbus_interface_t a;
142     int r;
143     sd_bus_error error = SD_BUS_ERROR_NULL;
144     sd_bus_message* m = NULL;
145 
146     r = find_openbmc_path(number, &a);
147 
148     if (r < 0)
149     {
150         std::fprintf(stderr, "Failed to find Sensor 0x%02x\n", number);
151         return 0;
152     }
153 
154     r = sd_bus_message_new_method_call(bus, &m, a.bus, a.path, a.interface,
155                                        method);
156     if (r < 0)
157     {
158         std::fprintf(stderr, "Failed to create a method call: %s",
159                      strerror(-r));
160         goto final;
161     }
162 
163     r = sd_bus_message_append(m, "v", "s", value);
164     if (r < 0)
165     {
166         std::fprintf(stderr, "Failed to create a input parameter: %s",
167                      strerror(-r));
168         goto final;
169     }
170 
171     r = sd_bus_call(bus, m, 0, &error, NULL);
172     if (r < 0)
173     {
174         std::fprintf(stderr, "Failed to call the method: %s", strerror(-r));
175     }
176 
177 final:
178     sd_bus_error_free(&error);
179     m = sd_bus_message_unref(m);
180 
181     return 0;
182 }
183 int set_sensor_dbus_state_y(uint8_t number, const char* method,
184                             const uint8_t value)
185 {
186 
187     dbus_interface_t a;
188     int r;
189     sd_bus_error error = SD_BUS_ERROR_NULL;
190     sd_bus_message* m = NULL;
191 
192     r = find_openbmc_path(number, &a);
193 
194     if (r < 0)
195     {
196         std::fprintf(stderr, "Failed to find Sensor 0x%02x\n", number);
197         return 0;
198     }
199 
200     r = sd_bus_message_new_method_call(bus, &m, a.bus, a.path, a.interface,
201                                        method);
202     if (r < 0)
203     {
204         std::fprintf(stderr, "Failed to create a method call: %s",
205                      strerror(-r));
206         goto final;
207     }
208 
209     r = sd_bus_message_append(m, "v", "i", value);
210     if (r < 0)
211     {
212         std::fprintf(stderr, "Failed to create a input parameter: %s",
213                      strerror(-r));
214         goto final;
215     }
216 
217     r = sd_bus_call(bus, m, 0, &error, NULL);
218     if (r < 0)
219     {
220         std::fprintf(stderr, "12 Failed to call the method: %s", strerror(-r));
221     }
222 
223 final:
224     sd_bus_error_free(&error);
225     m = sd_bus_message_unref(m);
226 
227     return 0;
228 }
229 
230 uint8_t dbus_to_sensor_type(char* p)
231 {
232 
233     sensorTypemap_t* s = g_SensorTypeMap;
234     char r = 0;
235     while (s->number != 0xFF)
236     {
237         if (!strcmp(s->dbusname, p))
238         {
239             r = s->typecode;
240             break;
241         }
242         s++;
243     }
244 
245     if (s->number == 0xFF)
246         printf("Failed to find Sensor Type %s\n", p);
247 
248     return r;
249 }
250 
251 uint8_t get_type_from_interface(dbus_interface_t dbus_if)
252 {
253 
254     uint8_t type;
255 
256     // This is where sensors that do not exist in dbus but do
257     // exist in the host code stop.  This should indicate it
258     // is not a supported sensor
259     if (dbus_if.interface[0] == 0)
260     {
261         return 0;
262     }
263 
264     // Fetch type from interface itself.
265     if (dbus_if.sensortype != 0)
266     {
267         type = dbus_if.sensortype;
268     }
269     else
270     {
271         // Non InventoryItems
272         char* p = strrchr(dbus_if.path, '/');
273         type = dbus_to_sensor_type(p + 1);
274     }
275 
276     return type;
277 }
278 
279 // Replaces find_sensor
280 uint8_t find_type_for_sensor_number(uint8_t num)
281 {
282     int r;
283     dbus_interface_t dbus_if;
284     r = find_openbmc_path(num, &dbus_if);
285     if (r < 0)
286     {
287         std::fprintf(stderr, "Could not find sensor %d\n", num);
288         return 0;
289     }
290     return get_type_from_interface(dbus_if);
291 }
292 
293 ipmi_ret_t ipmi_sen_get_sensor_type(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
294                                     ipmi_request_t request,
295                                     ipmi_response_t response,
296                                     ipmi_data_len_t data_len,
297                                     ipmi_context_t context)
298 {
299     auto reqptr = static_cast<sensor_data_t*>(request);
300     ipmi_ret_t rc = IPMI_CC_OK;
301 
302     printf("IPMI GET_SENSOR_TYPE [0x%02X]\n", reqptr->sennum);
303 
304     // TODO Not sure what the System-event-sensor is suppose to return
305     // need to ask Hostboot team
306     unsigned char buf[] = {0x00, 0x6F};
307 
308     buf[0] = find_type_for_sensor_number(reqptr->sennum);
309 
310     // HACK UNTIL Dbus gets updated or we find a better way
311     if (buf[0] == 0)
312     {
313         rc = IPMI_CC_SENSOR_INVALID;
314     }
315 
316     *data_len = sizeof(buf);
317     std::memcpy(response, &buf, *data_len);
318 
319     return rc;
320 }
321 
322 const std::set<std::string> analogSensorInterfaces = {
323     "xyz.openbmc_project.Sensor.Value",
324     "xyz.openbmc_project.Control.FanPwm",
325 };
326 
327 bool isAnalogSensor(const std::string& interface)
328 {
329     return (analogSensorInterfaces.count(interface));
330 }
331 
332 /**
333 @brief This command is used to set sensorReading.
334 
335 @param
336     -  sensorNumber
337     -  operation
338     -  reading
339     -  assertOffset0_7
340     -  assertOffset8_14
341     -  deassertOffset0_7
342     -  deassertOffset8_14
343     -  eventData1
344     -  eventData2
345     -  eventData3
346 
347 @return completion code on success.
348 **/
349 
350 ipmi::RspType<> ipmiSetSensorReading(uint8_t sensorNumber, uint8_t operation,
351                                      uint8_t reading, uint8_t assertOffset0_7,
352                                      uint8_t assertOffset8_14,
353                                      uint8_t deassertOffset0_7,
354                                      uint8_t deassertOffset8_14,
355                                      uint8_t eventData1, uint8_t eventData2,
356                                      uint8_t eventData3)
357 {
358     log<level::DEBUG>("IPMI SET_SENSOR",
359                       entry("SENSOR_NUM=0x%02x", sensorNumber));
360 
361     ipmi::sensor::SetSensorReadingReq cmdData;
362 
363     cmdData.number = sensorNumber;
364     cmdData.operation = operation;
365     cmdData.reading = reading;
366     cmdData.assertOffset0_7 = assertOffset0_7;
367     cmdData.assertOffset8_14 = assertOffset8_14;
368     cmdData.deassertOffset0_7 = deassertOffset0_7;
369     cmdData.deassertOffset8_14 = deassertOffset8_14;
370     cmdData.eventData1 = eventData1;
371     cmdData.eventData2 = eventData2;
372     cmdData.eventData3 = eventData3;
373 
374     // Check if the Sensor Number is present
375     const auto iter = sensors.find(sensorNumber);
376     if (iter == sensors.end())
377     {
378         updateSensorRecordFromSSRAESC(&sensorNumber);
379         return ipmi::responseSuccess();
380     }
381 
382     try
383     {
384         if (ipmi::sensor::Mutability::Write !=
385             (iter->second.mutability & ipmi::sensor::Mutability::Write))
386         {
387             log<level::ERR>("Sensor Set operation is not allowed",
388                             entry("SENSOR_NUM=%d", sensorNumber));
389             return ipmi::responseIllegalCommand();
390         }
391         auto ipmiRC = iter->second.updateFunc(cmdData, iter->second);
392         return ipmi::response(ipmiRC);
393     }
394     catch (InternalFailure& e)
395     {
396         log<level::ERR>("Set sensor failed",
397                         entry("SENSOR_NUM=%d", sensorNumber));
398         commit<InternalFailure>();
399         return ipmi::responseUnspecifiedError();
400     }
401     catch (const std::runtime_error& e)
402     {
403         log<level::ERR>(e.what());
404         return ipmi::responseUnspecifiedError();
405     }
406 }
407 
408 ipmi_ret_t ipmi_sen_get_sensor_reading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
409                                        ipmi_request_t request,
410                                        ipmi_response_t response,
411                                        ipmi_data_len_t data_len,
412                                        ipmi_context_t context)
413 {
414     auto reqptr = static_cast<sensor_data_t*>(request);
415     auto resp = static_cast<sensorreadingresp_t*>(response);
416     ipmi::sensor::GetSensorResponse getResponse{};
417     static constexpr auto scanningEnabledBit = 6;
418 
419     const auto iter = sensors.find(reqptr->sennum);
420     if (iter == sensors.end())
421     {
422         return IPMI_CC_SENSOR_INVALID;
423     }
424     if (ipmi::sensor::Mutability::Read !=
425         (iter->second.mutability & ipmi::sensor::Mutability::Read))
426     {
427         return IPMI_CC_ILLEGAL_COMMAND;
428     }
429 
430     try
431     {
432         getResponse = iter->second.getFunc(iter->second);
433         *data_len = getResponse.size();
434         std::memcpy(resp, getResponse.data(), *data_len);
435         resp->operation = 1 << scanningEnabledBit;
436         return IPMI_CC_OK;
437     }
438 #ifdef UPDATE_FUNCTIONAL_ON_FAIL
439     catch (const SensorFunctionalError& e)
440     {
441         return IPMI_CC_RESPONSE_ERROR;
442     }
443 #endif
444     catch (const std::exception& e)
445     {
446         *data_len = getResponse.size();
447         std::memcpy(resp, getResponse.data(), *data_len);
448         return IPMI_CC_OK;
449     }
450 }
451 
452 void getSensorThresholds(uint8_t sensorNum,
453                          get_sdr::GetSensorThresholdsResponse* response)
454 {
455     constexpr auto warningThreshIntf =
456         "xyz.openbmc_project.Sensor.Threshold.Warning";
457     constexpr auto criticalThreshIntf =
458         "xyz.openbmc_project.Sensor.Threshold.Critical";
459 
460     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
461 
462     const auto iter = sensors.find(sensorNum);
463     const auto info = iter->second;
464 
465     auto service = ipmi::getService(bus, info.sensorInterface, info.sensorPath);
466 
467     auto warnThresholds = ipmi::getAllDbusProperties(
468         bus, service, info.sensorPath, warningThreshIntf);
469 
470     double warnLow = std::visit(ipmi::VariantToDoubleVisitor(),
471                                 warnThresholds["WarningLow"]);
472     double warnHigh = std::visit(ipmi::VariantToDoubleVisitor(),
473                                  warnThresholds["WarningHigh"]);
474 
475     if (warnLow != 0)
476     {
477         warnLow *= std::pow(10, info.scale - info.exponentR);
478         response->lowerNonCritical = static_cast<uint8_t>(
479             (warnLow - info.scaledOffset) / info.coefficientM);
480         response->validMask |= static_cast<uint8_t>(
481             ipmi::sensor::ThresholdMask::NON_CRITICAL_LOW_MASK);
482     }
483 
484     if (warnHigh != 0)
485     {
486         warnHigh *= std::pow(10, info.scale - info.exponentR);
487         response->upperNonCritical = static_cast<uint8_t>(
488             (warnHigh - info.scaledOffset) / info.coefficientM);
489         response->validMask |= static_cast<uint8_t>(
490             ipmi::sensor::ThresholdMask::NON_CRITICAL_HIGH_MASK);
491     }
492 
493     auto critThresholds = ipmi::getAllDbusProperties(
494         bus, service, info.sensorPath, criticalThreshIntf);
495     double critLow = std::visit(ipmi::VariantToDoubleVisitor(),
496                                 critThresholds["CriticalLow"]);
497     double critHigh = std::visit(ipmi::VariantToDoubleVisitor(),
498                                  critThresholds["CriticalHigh"]);
499 
500     if (critLow != 0)
501     {
502         critLow *= std::pow(10, info.scale - info.exponentR);
503         response->lowerCritical = static_cast<uint8_t>(
504             (critLow - info.scaledOffset) / info.coefficientM);
505         response->validMask |= static_cast<uint8_t>(
506             ipmi::sensor::ThresholdMask::CRITICAL_LOW_MASK);
507     }
508 
509     if (critHigh != 0)
510     {
511         critHigh *= std::pow(10, info.scale - info.exponentR);
512         response->upperCritical = static_cast<uint8_t>(
513             (critHigh - info.scaledOffset) / info.coefficientM);
514         response->validMask |= static_cast<uint8_t>(
515             ipmi::sensor::ThresholdMask::CRITICAL_HIGH_MASK);
516     }
517 }
518 
519 ipmi_ret_t ipmi_sen_get_sensor_thresholds(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
520                                           ipmi_request_t request,
521                                           ipmi_response_t response,
522                                           ipmi_data_len_t data_len,
523                                           ipmi_context_t context)
524 {
525     constexpr auto valueInterface = "xyz.openbmc_project.Sensor.Value";
526 
527     if (*data_len != sizeof(uint8_t))
528     {
529         *data_len = 0;
530         return IPMI_CC_REQ_DATA_LEN_INVALID;
531     }
532 
533     auto sensorNum = *(reinterpret_cast<const uint8_t*>(request));
534     *data_len = 0;
535 
536     const auto iter = sensors.find(sensorNum);
537     if (iter == sensors.end())
538     {
539         return IPMI_CC_SENSOR_INVALID;
540     }
541 
542     const auto info = iter->second;
543 
544     // Proceed only if the sensor value interface is implemented.
545     if (info.propertyInterfaces.find(valueInterface) ==
546         info.propertyInterfaces.end())
547     {
548         // return with valid mask as 0
549         return IPMI_CC_OK;
550     }
551 
552     auto responseData =
553         reinterpret_cast<get_sdr::GetSensorThresholdsResponse*>(response);
554 
555     try
556     {
557         getSensorThresholds(sensorNum, responseData);
558     }
559     catch (std::exception& e)
560     {
561         // Mask if the property is not present
562         responseData->validMask = 0;
563     }
564 
565     *data_len = sizeof(get_sdr::GetSensorThresholdsResponse);
566     return IPMI_CC_OK;
567 }
568 
569 ipmi_ret_t ipmi_sen_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
570                              ipmi_request_t request, ipmi_response_t response,
571                              ipmi_data_len_t data_len, ipmi_context_t context)
572 {
573     ipmi_ret_t rc = IPMI_CC_INVALID;
574 
575     printf("IPMI S/E Wildcard Netfn:[0x%X], Cmd:[0x%X]\n", netfn, cmd);
576     *data_len = 0;
577 
578     return rc;
579 }
580 
581 /** @brief implements the get SDR Info command
582  *  @param count - Operation
583  *
584  *  @returns IPMI completion code plus response data
585  *   - sdrCount - sensor/SDR count
586  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
587  */
588 ipmi::RspType<uint8_t, // respcount
589               uint8_t  // dynamic population flags
590               >
591     ipmiSensorGetDeviceSdrInfo(std::optional<uint8_t> count)
592 {
593     uint8_t sdrCount;
594     // multiple LUNs not supported.
595     constexpr uint8_t lunsAndDynamicPopulation = 1;
596     constexpr uint8_t getSdrCount = 0x01;
597     constexpr uint8_t getSensorCount = 0x00;
598 
599     if (count.value_or(0) == getSdrCount)
600     {
601         // Get SDR count. This returns the total number of SDRs in the device.
602         const auto& entityRecords = getIpmiEntityRecords();
603         sdrCount = sensors.size() + frus.size() + entityRecords.size();
604     }
605     else if (count.value_or(0) == getSensorCount)
606     {
607         // Get Sensor count. This returns the number of sensors
608         sdrCount = sensors.size();
609     }
610     else
611     {
612         return ipmi::responseInvalidCommandOnLun();
613     }
614 
615     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation);
616 }
617 
618 /** @brief implements the reserve SDR command
619  *  @returns IPMI completion code plus response data
620  *   - reservationID - reservation ID
621  */
622 ipmi::RspType<uint16_t> ipmiSensorReserveSdr()
623 {
624     // A constant reservation ID is okay until we implement add/remove SDR.
625     constexpr uint16_t reservationID = 1;
626 
627     return ipmi::responseSuccess(reservationID);
628 }
629 
630 void setUnitFieldsForObject(const ipmi::sensor::Info* info,
631                             get_sdr::SensorDataFullRecordBody* body)
632 {
633     namespace server = sdbusplus::xyz::openbmc_project::Sensor::server;
634     try
635     {
636         auto unit = server::Value::convertUnitFromString(info->unit);
637         // Unit strings defined in
638         // phosphor-dbus-interfaces/xyz/openbmc_project/Sensor/Value.interface.yaml
639         switch (unit)
640         {
641             case server::Value::Unit::DegreesC:
642                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_DEGREES_C;
643                 break;
644             case server::Value::Unit::RPMS:
645                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_RPM;
646                 break;
647             case server::Value::Unit::Volts:
648                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_VOLTS;
649                 break;
650             case server::Value::Unit::Meters:
651                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_METERS;
652                 break;
653             case server::Value::Unit::Amperes:
654                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_AMPERES;
655                 break;
656             case server::Value::Unit::Joules:
657                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_JOULES;
658                 break;
659             case server::Value::Unit::Watts:
660                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_WATTS;
661                 break;
662             default:
663                 // Cannot be hit.
664                 std::fprintf(stderr, "Unknown value unit type: = %s\n",
665                              info->unit.c_str());
666         }
667     }
668     catch (const sdbusplus::exception::InvalidEnumString& e)
669     {
670         log<level::WARNING>("Warning: no unit provided for sensor!");
671     }
672 }
673 
674 ipmi_ret_t populate_record_from_dbus(get_sdr::SensorDataFullRecordBody* body,
675                                      const ipmi::sensor::Info* info,
676                                      ipmi_data_len_t data_len)
677 {
678     /* Functional sensor case */
679     if (isAnalogSensor(info->propertyInterfaces.begin()->first))
680     {
681 
682         body->sensor_units_1 = 0; // unsigned, no rate, no modifier, not a %
683 
684         /* Unit info */
685         setUnitFieldsForObject(info, body);
686 
687         get_sdr::body::set_b(info->coefficientB, body);
688         get_sdr::body::set_m(info->coefficientM, body);
689         get_sdr::body::set_b_exp(info->exponentB, body);
690         get_sdr::body::set_r_exp(info->exponentR, body);
691 
692         get_sdr::body::set_id_type(0b00, body); // 00 = unicode
693     }
694 
695     /* ID string */
696     auto id_string = info->sensorNameFunc(*info);
697 
698     if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
699     {
700         get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH, body);
701     }
702     else
703     {
704         get_sdr::body::set_id_strlen(id_string.length(), body);
705     }
706     strncpy(body->id_string, id_string.c_str(),
707             get_sdr::body::get_id_strlen(body));
708 
709     return IPMI_CC_OK;
710 };
711 
712 ipmi_ret_t ipmi_fru_get_sdr(ipmi_request_t request, ipmi_response_t response,
713                             ipmi_data_len_t data_len)
714 {
715     auto req = reinterpret_cast<get_sdr::GetSdrReq*>(request);
716     auto resp = reinterpret_cast<get_sdr::GetSdrResp*>(response);
717     get_sdr::SensorDataFruRecord record{};
718     auto dataLength = 0;
719 
720     auto fru = frus.begin();
721     uint8_t fruID{};
722     auto recordID = get_sdr::request::get_record_id(req);
723 
724     fruID = recordID - FRU_RECORD_ID_START;
725     fru = frus.find(fruID);
726     if (fru == frus.end())
727     {
728         return IPMI_CC_SENSOR_INVALID;
729     }
730 
731     /* Header */
732     get_sdr::header::set_record_id(recordID, &(record.header));
733     record.header.sdr_version = SDR_VERSION; // Based on IPMI Spec v2.0 rev 1.1
734     record.header.record_type = get_sdr::SENSOR_DATA_FRU_RECORD;
735     record.header.record_length = sizeof(record.key) + sizeof(record.body);
736 
737     /* Key */
738     record.key.fruID = fruID;
739     record.key.accessLun |= IPMI_LOGICAL_FRU;
740     record.key.deviceAddress = BMCSlaveAddress;
741 
742     /* Body */
743     record.body.entityID = fru->second[0].entityID;
744     record.body.entityInstance = fru->second[0].entityInstance;
745     record.body.deviceType = fruInventoryDevice;
746     record.body.deviceTypeModifier = IPMIFruInventory;
747 
748     /* Device ID string */
749     auto deviceID =
750         fru->second[0].path.substr(fru->second[0].path.find_last_of('/') + 1,
751                                    fru->second[0].path.length());
752 
753     if (deviceID.length() > get_sdr::FRU_RECORD_DEVICE_ID_MAX_LENGTH)
754     {
755         get_sdr::body::set_device_id_strlen(
756             get_sdr::FRU_RECORD_DEVICE_ID_MAX_LENGTH, &(record.body));
757     }
758     else
759     {
760         get_sdr::body::set_device_id_strlen(deviceID.length(), &(record.body));
761     }
762 
763     strncpy(record.body.deviceID, deviceID.c_str(),
764             get_sdr::body::get_device_id_strlen(&(record.body)));
765 
766     if (++fru == frus.end())
767     {
768         // we have reached till end of fru, so assign the next record id to
769         // 512(Max fru ID = 511) + Entity Record ID(may start with 0).
770         const auto& entityRecords = getIpmiEntityRecords();
771         auto next_record_id =
772             (entityRecords.size())
773                 ? entityRecords.begin()->first + ENTITY_RECORD_ID_START
774                 : END_OF_RECORD;
775         get_sdr::response::set_next_record_id(next_record_id, resp);
776     }
777     else
778     {
779         get_sdr::response::set_next_record_id(
780             (FRU_RECORD_ID_START + fru->first), resp);
781     }
782 
783     // Check for invalid offset size
784     if (req->offset > sizeof(record))
785     {
786         return IPMI_CC_PARM_OUT_OF_RANGE;
787     }
788 
789     dataLength = std::min(static_cast<size_t>(req->bytes_to_read),
790                           sizeof(record) - req->offset);
791 
792     std::memcpy(resp->record_data,
793                 reinterpret_cast<uint8_t*>(&record) + req->offset, dataLength);
794 
795     *data_len = dataLength;
796     *data_len += 2; // additional 2 bytes for next record ID
797 
798     return IPMI_CC_OK;
799 }
800 
801 ipmi_ret_t ipmi_entity_get_sdr(ipmi_request_t request, ipmi_response_t response,
802                                ipmi_data_len_t data_len)
803 {
804     auto req = reinterpret_cast<get_sdr::GetSdrReq*>(request);
805     auto resp = reinterpret_cast<get_sdr::GetSdrResp*>(response);
806     get_sdr::SensorDataEntityRecord record{};
807     auto dataLength = 0;
808 
809     const auto& entityRecords = getIpmiEntityRecords();
810     auto entity = entityRecords.begin();
811     uint8_t entityRecordID;
812     auto recordID = get_sdr::request::get_record_id(req);
813 
814     entityRecordID = recordID - ENTITY_RECORD_ID_START;
815     entity = entityRecords.find(entityRecordID);
816     if (entity == entityRecords.end())
817     {
818         return IPMI_CC_SENSOR_INVALID;
819     }
820 
821     /* Header */
822     get_sdr::header::set_record_id(recordID, &(record.header));
823     record.header.sdr_version = SDR_VERSION; // Based on IPMI Spec v2.0 rev 1.1
824     record.header.record_type = get_sdr::SENSOR_DATA_ENTITY_RECORD;
825     record.header.record_length = sizeof(record.key) + sizeof(record.body);
826 
827     /* Key */
828     record.key.containerEntityId = entity->second.containerEntityId;
829     record.key.containerEntityInstance = entity->second.containerEntityInstance;
830     get_sdr::key::set_flags(entity->second.isList, entity->second.isLinked,
831                             &(record.key));
832     record.key.entityId1 = entity->second.containedEntities[0].first;
833     record.key.entityInstance1 = entity->second.containedEntities[0].second;
834 
835     /* Body */
836     record.body.entityId2 = entity->second.containedEntities[1].first;
837     record.body.entityInstance2 = entity->second.containedEntities[1].second;
838     record.body.entityId3 = entity->second.containedEntities[2].first;
839     record.body.entityInstance3 = entity->second.containedEntities[2].second;
840     record.body.entityId4 = entity->second.containedEntities[3].first;
841     record.body.entityInstance4 = entity->second.containedEntities[3].second;
842 
843     if (++entity == entityRecords.end())
844     {
845         get_sdr::response::set_next_record_id(END_OF_RECORD,
846                                               resp); // last record
847     }
848     else
849     {
850         get_sdr::response::set_next_record_id(
851             (ENTITY_RECORD_ID_START + entity->first), resp);
852     }
853 
854     // Check for invalid offset size
855     if (req->offset > sizeof(record))
856     {
857         return IPMI_CC_PARM_OUT_OF_RANGE;
858     }
859 
860     dataLength = std::min(static_cast<size_t>(req->bytes_to_read),
861                           sizeof(record) - req->offset);
862 
863     std::memcpy(resp->record_data,
864                 reinterpret_cast<uint8_t*>(&record) + req->offset, dataLength);
865 
866     *data_len = dataLength;
867     *data_len += 2; // additional 2 bytes for next record ID
868 
869     return IPMI_CC_OK;
870 }
871 
872 ipmi_ret_t ipmi_sen_get_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
873                             ipmi_request_t request, ipmi_response_t response,
874                             ipmi_data_len_t data_len, ipmi_context_t context)
875 {
876     ipmi_ret_t ret = IPMI_CC_OK;
877     get_sdr::GetSdrReq* req = (get_sdr::GetSdrReq*)request;
878     get_sdr::GetSdrResp* resp = (get_sdr::GetSdrResp*)response;
879     get_sdr::SensorDataFullRecord record = {0};
880 
881     // Note: we use an iterator so we can provide the next ID at the end of
882     // the call.
883     auto sensor = sensors.begin();
884     auto recordID = get_sdr::request::get_record_id(req);
885 
886     // At the beginning of a scan, the host side will send us id=0.
887     if (recordID != 0)
888     {
889         // recordID 0 to 255 means it is a FULL record.
890         // recordID 256 to 511 means it is a FRU record.
891         // recordID greater then 511 means it is a Entity Association
892         // record. Currently we are supporting three record types: FULL
893         // record, FRU record and Enttiy Association record.
894         if (recordID >= ENTITY_RECORD_ID_START)
895         {
896             return ipmi_entity_get_sdr(request, response, data_len);
897         }
898         else if (recordID >= FRU_RECORD_ID_START &&
899                  recordID < ENTITY_RECORD_ID_START)
900         {
901             return ipmi_fru_get_sdr(request, response, data_len);
902         }
903         else
904         {
905             sensor = sensors.find(recordID);
906             if (sensor == sensors.end())
907             {
908                 return IPMI_CC_SENSOR_INVALID;
909             }
910         }
911     }
912 
913     uint8_t sensor_id = sensor->first;
914 
915     /* Header */
916     get_sdr::header::set_record_id(sensor_id, &(record.header));
917     record.header.sdr_version = 0x51; // Based on IPMI Spec v2.0 rev 1.1
918     record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
919     record.header.record_length = sizeof(get_sdr::SensorDataFullRecord);
920 
921     /* Key */
922     get_sdr::key::set_owner_id_bmc(&(record.key));
923     record.key.sensor_number = sensor_id;
924 
925     /* Body */
926     record.body.entity_id = sensor->second.entityType;
927     record.body.sensor_type = sensor->second.sensorType;
928     record.body.event_reading_type = sensor->second.sensorReadingType;
929     record.body.entity_instance = sensor->second.instance;
930     if (ipmi::sensor::Mutability::Write ==
931         (sensor->second.mutability & ipmi::sensor::Mutability::Write))
932     {
933         get_sdr::body::init_settable_state(true, &(record.body));
934     }
935 
936     // Set the type-specific details given the DBus interface
937     ret =
938         populate_record_from_dbus(&(record.body), &(sensor->second), data_len);
939 
940     if (++sensor == sensors.end())
941     {
942         // we have reached till end of sensor, so assign the next record id
943         // to 256(Max Sensor ID = 255) + FRU ID(may start with 0).
944         auto next_record_id = (frus.size())
945                                   ? frus.begin()->first + FRU_RECORD_ID_START
946                                   : END_OF_RECORD;
947 
948         get_sdr::response::set_next_record_id(next_record_id, resp);
949     }
950     else
951     {
952         get_sdr::response::set_next_record_id(sensor->first, resp);
953     }
954 
955     if (req->offset > sizeof(record))
956     {
957         return IPMI_CC_PARM_OUT_OF_RANGE;
958     }
959 
960     // data_len will ultimately be the size of the record, plus
961     // the size of the next record ID:
962     *data_len = std::min(static_cast<size_t>(req->bytes_to_read),
963                          sizeof(record) - req->offset);
964 
965     std::memcpy(resp->record_data,
966                 reinterpret_cast<uint8_t*>(&record) + req->offset, *data_len);
967 
968     // data_len should include the LSB and MSB:
969     *data_len +=
970         sizeof(resp->next_record_id_lsb) + sizeof(resp->next_record_id_msb);
971 
972     return ret;
973 }
974 
975 static bool isFromSystemChannel()
976 {
977     // TODO we could not figure out where the request is from based on IPMI
978     // command handler parameters. because of it, we can not differentiate
979     // request from SMS/SMM or IPMB channel
980     return true;
981 }
982 
983 ipmi_ret_t ipmicmdPlatformEvent(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
984                                 ipmi_request_t request,
985                                 ipmi_response_t response,
986                                 ipmi_data_len_t dataLen, ipmi_context_t context)
987 {
988     uint16_t generatorID;
989     size_t count;
990     bool assert = true;
991     std::string sensorPath;
992     size_t paraLen = *dataLen;
993     PlatformEventRequest* req;
994     *dataLen = 0;
995 
996     if ((paraLen < selSystemEventSizeWith1Bytes) ||
997         (paraLen > selSystemEventSizeWith3Bytes))
998     {
999         return IPMI_CC_REQ_DATA_LEN_INVALID;
1000     }
1001 
1002     if (isFromSystemChannel())
1003     { // first byte for SYSTEM Interface is Generator ID
1004         // +1 to get common struct
1005         req = reinterpret_cast<PlatformEventRequest*>((uint8_t*)request + 1);
1006         // Capture the generator ID
1007         generatorID = *reinterpret_cast<uint8_t*>(request);
1008         // Platform Event usually comes from other firmware, like BIOS.
1009         // Unlike BMC sensor, it does not have BMC DBUS sensor path.
1010         sensorPath = "System";
1011     }
1012     else
1013     {
1014         req = reinterpret_cast<PlatformEventRequest*>(request);
1015         // TODO GenratorID for IPMB is combination of RqSA and RqLUN
1016         generatorID = 0xff;
1017         sensorPath = "IPMB";
1018     }
1019     // Content of event data field depends on sensor class.
1020     // When data0 bit[5:4] is non-zero, valid data counts is 3.
1021     // When data0 bit[7:6] is non-zero, valid data counts is 2.
1022     if (((req->data[0] & byte3EnableMask) != 0 &&
1023          paraLen < selSystemEventSizeWith3Bytes) ||
1024         ((req->data[0] & byte2EnableMask) != 0 &&
1025          paraLen < selSystemEventSizeWith2Bytes))
1026     {
1027         return IPMI_CC_REQ_DATA_LEN_INVALID;
1028     }
1029 
1030     // Count bytes of Event Data
1031     if ((req->data[0] & byte3EnableMask) != 0)
1032     {
1033         count = 3;
1034     }
1035     else if ((req->data[0] & byte2EnableMask) != 0)
1036     {
1037         count = 2;
1038     }
1039     else
1040     {
1041         count = 1;
1042     }
1043     assert = req->eventDirectionType & directionMask ? false : true;
1044     std::vector<uint8_t> eventData(req->data, req->data + count);
1045 
1046     sdbusplus::bus::bus dbus(bus);
1047     std::string service =
1048         ipmi::getService(dbus, ipmiSELAddInterface, ipmiSELPath);
1049     sdbusplus::message::message writeSEL = dbus.new_method_call(
1050         service.c_str(), ipmiSELPath, ipmiSELAddInterface, "IpmiSelAdd");
1051     writeSEL.append(ipmiSELAddMessage, sensorPath, eventData, assert,
1052                     generatorID);
1053     try
1054     {
1055         dbus.call(writeSEL);
1056     }
1057     catch (sdbusplus::exception_t& e)
1058     {
1059         phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
1060         return IPMI_CC_UNSPECIFIED_ERROR;
1061     }
1062     return IPMI_CC_OK;
1063 }
1064 
1065 void register_netfn_sen_functions()
1066 {
1067     // <Wildcard Command>
1068     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_WILDCARD, nullptr,
1069                            ipmi_sen_wildcard, PRIVILEGE_USER);
1070 
1071     // <Platform Event Message>
1072     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_PLATFORM_EVENT, nullptr,
1073                            ipmicmdPlatformEvent, PRIVILEGE_OPERATOR);
1074     // <Get Sensor Type>
1075     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_TYPE, nullptr,
1076                            ipmi_sen_get_sensor_type, PRIVILEGE_USER);
1077 
1078     // <Set Sensor Reading and Event Status>
1079     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1080                           ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
1081                           ipmi::Privilege::Operator, ipmiSetSensorReading);
1082     // <Get Sensor Reading>
1083     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_READING, nullptr,
1084                            ipmi_sen_get_sensor_reading, PRIVILEGE_USER);
1085 
1086     // <Reserve Device SDR Repository>
1087     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1088                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
1089                           ipmi::Privilege::User, ipmiSensorReserveSdr);
1090 
1091     // <Get Device SDR Info>
1092     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1093                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
1094                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
1095 
1096     // <Get Device SDR>
1097     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_DEVICE_SDR, nullptr,
1098                            ipmi_sen_get_sdr, PRIVILEGE_USER);
1099 
1100     // <Get Sensor Thresholds>
1101     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_THRESHOLDS,
1102                            nullptr, ipmi_sen_get_sensor_thresholds,
1103                            PRIVILEGE_USER);
1104 
1105     return;
1106 }
1107