xref: /openbmc/phosphor-host-ipmid/sensorhandler.cpp (revision 653aec092d86597aa7d7d1259eee58e12310f202)
1 #include "config.h"
2 
3 #include "sensorhandler.hpp"
4 
5 #include "fruread.hpp"
6 
7 #include <systemd/sd-bus.h>
8 
9 #include <ipmid/api.hpp>
10 #include <ipmid/entity_map_json.hpp>
11 #include <ipmid/types.hpp>
12 #include <ipmid/utils.hpp>
13 #include <phosphor-logging/elog-errors.hpp>
14 #include <phosphor-logging/lg2.hpp>
15 #include <sdbusplus/message/types.hpp>
16 #include <xyz/openbmc_project/Common/error.hpp>
17 #include <xyz/openbmc_project/Sensor/Threshold/Critical/common.hpp>
18 #include <xyz/openbmc_project/Sensor/Threshold/Warning/common.hpp>
19 #include <xyz/openbmc_project/Sensor/Value/server.hpp>
20 
21 #include <bitset>
22 #include <cmath>
23 #include <cstring>
24 #include <set>
25 
26 static constexpr uint8_t fruInventoryDevice = 0x10;
27 static constexpr uint8_t IPMIFruInventory = 0x02;
28 static constexpr uint8_t BMCTargetAddress = 0x20;
29 
30 extern int updateSensorRecordFromSSRAESC(const void*);
31 extern sd_bus* bus;
32 
33 namespace ipmi
34 {
35 namespace sensor
36 {
37 extern const IdInfoMap sensors;
38 } // namespace sensor
39 } // namespace ipmi
40 
41 extern const FruMap frus;
42 
43 using namespace phosphor::logging;
44 using InternalFailure =
45     sdbusplus::error::xyz::openbmc_project::common::InternalFailure;
46 
47 using SensorValue = sdbusplus::common::xyz::openbmc_project::sensor::Value;
48 using SensorThresholdCritical =
49     sdbusplus::common::xyz::openbmc_project::sensor::threshold::Critical;
50 using SensorThresholdWarning =
51     sdbusplus::common::xyz::openbmc_project::sensor::threshold::Warning;
52 
53 void registerNetFnSenFunctions() __attribute__((constructor));
54 
55 struct sensorTypemap_t
56 {
57     uint8_t number;
58     uint8_t typecode;
59     char dbusname[32];
60 };
61 
62 sensorTypemap_t g_SensorTypeMap[] = {
63 
64     {0x01, 0x6F, "Temp"},
65     {0x0C, 0x6F, "DIMM"},
66     {0x0C, 0x6F, "MEMORY_BUFFER"},
67     {0x07, 0x6F, "PROC"},
68     {0x07, 0x6F, "CORE"},
69     {0x07, 0x6F, "CPU"},
70     {0x0F, 0x6F, "BootProgress"},
71     {0xe9, 0x09, "OccStatus"}, // E9 is an internal mapping to handle sensor
72                                // type code os 0x09
73     {0xC3, 0x6F, "BootCount"},
74     {0x1F, 0x6F, "OperatingSystemStatus"},
75     {0x12, 0x6F, "SYSTEM_EVENT"},
76     {0xC7, 0x03, "SYSTEM"},
77     {0xC7, 0x03, "MAIN_PLANAR"},
78     {0xC2, 0x6F, "PowerCap"},
79     {0x0b, 0xCA, "PowerSupplyRedundancy"},
80     {0xDA, 0x03, "TurboAllowed"},
81     {0xD8, 0xC8, "PowerSupplyDerating"},
82     {0xFF, 0x00, ""},
83 };
84 
85 struct sensor_data_t
86 {
87     uint8_t sennum;
88 } __attribute__((packed));
89 
90 using SDRCacheMap = std::unordered_map<uint8_t, get_sdr::SensorDataFullRecord>;
91 SDRCacheMap sdrCacheMap __attribute__((init_priority(101)));
92 
93 using SensorThresholdMap =
94     std::unordered_map<uint8_t, get_sdr::GetSensorThresholdsResponse>;
95 SensorThresholdMap sensorThresholdMap __attribute__((init_priority(101)));
96 
97 #ifdef FEATURE_SENSORS_CACHE
98 std::map<uint8_t, std::unique_ptr<sdbusplus::bus::match_t>> sensorAddedMatches
99     __attribute__((init_priority(101)));
100 std::map<uint8_t, std::unique_ptr<sdbusplus::bus::match_t>> sensorUpdatedMatches
101     __attribute__((init_priority(101)));
102 std::map<uint8_t, std::unique_ptr<sdbusplus::bus::match_t>> sensorRemovedMatches
103     __attribute__((init_priority(101)));
104 std::unique_ptr<sdbusplus::bus::match_t> sensorsOwnerMatch
105     __attribute__((init_priority(101)));
106 
107 ipmi::sensor::SensorCacheMap sensorCacheMap __attribute__((init_priority(101)));
108 
109 // It is needed to know which objects belong to which service, so that when a
110 // service exits without interfacesRemoved signal, we could invaildate the cache
111 // that is related to the service. It uses below two variables:
112 // - idToServiceMap records which sensors are known to have a related service;
113 // - serviceToIdMap maps a service to the sensors.
114 using sensorIdToServiceMap = std::unordered_map<uint8_t, std::string>;
115 sensorIdToServiceMap idToServiceMap __attribute__((init_priority(101)));
116 
117 using sensorServiceToIdMap = std::unordered_map<std::string, std::set<uint8_t>>;
118 sensorServiceToIdMap serviceToIdMap __attribute__((init_priority(101)));
119 
fillSensorIdServiceMap(const std::string &,const std::string &,uint8_t id,const std::string & service)120 static void fillSensorIdServiceMap(const std::string&,
121                                    const std::string& /*intf*/, uint8_t id,
122                                    const std::string& service)
123 {
124     if (idToServiceMap.find(id) != idToServiceMap.end())
125     {
126         return;
127     }
128     idToServiceMap[id] = service;
129     serviceToIdMap[service].insert(id);
130 }
131 
fillSensorIdServiceMap(const std::string & obj,const std::string & intf,uint8_t id)132 static void fillSensorIdServiceMap(const std::string& obj,
133                                    const std::string& intf, uint8_t id)
134 {
135     if (idToServiceMap.find(id) != idToServiceMap.end())
136     {
137         return;
138     }
139     try
140     {
141         sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
142         auto service = ipmi::getService(bus, intf, obj);
143         idToServiceMap[id] = service;
144         serviceToIdMap[service].insert(id);
145     }
146     catch (...)
147     {
148         // Ignore
149     }
150 }
151 
initSensorMatches()152 void initSensorMatches()
153 {
154     using namespace sdbusplus::bus::match::rules;
155     sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
156     for (const auto& s : ipmi::sensor::sensors)
157     {
158         sensorAddedMatches.emplace(
159             s.first,
160             std::make_unique<sdbusplus::bus::match_t>(
161                 bus, interfacesAdded() + argNpath(0, s.second.sensorPath),
162                 [id = s.first, obj = s.second.sensorPath,
163                  intf = s.second.propertyInterfaces.begin()->first](
164                     auto& /*msg*/) { fillSensorIdServiceMap(obj, intf, id); }));
165         sensorRemovedMatches.emplace(
166             s.first,
167             std::make_unique<sdbusplus::bus::match_t>(
168                 bus, interfacesRemoved() + argNpath(0, s.second.sensorPath),
169                 [id = s.first](auto& /*msg*/) {
170                     // Ideally this should work.
171                     // But when a service is terminated or crashed, it does not
172                     // emit interfacesRemoved signal. In that case it's handled
173                     // by sensorsOwnerMatch
174                     sensorCacheMap[id].reset();
175                 }));
176         sensorUpdatedMatches.emplace(
177             s.first,
178             std::make_unique<sdbusplus::bus::match_t>(
179                 bus,
180                 type::signal() + path(s.second.sensorPath) +
181                     member("PropertiesChanged"s) +
182                     interface("org.freedesktop.DBus.Properties"s),
183                 [&s](auto& msg) {
184                     fillSensorIdServiceMap(
185                         s.second.sensorPath,
186                         s.second.propertyInterfaces.begin()->first, s.first);
187                     try
188                     {
189                         // This is signal callback
190                         auto interfaceName = msg.unpack<std::string>();
191 
192                         auto props = msg.unpack<ipmi::PropertyMap>();
193 
194                         s.second.getFunc(s.first, s.second, props);
195                     }
196                     catch (const std::exception& e)
197                     {
198                         sensorCacheMap[s.first].reset();
199                     }
200                 }));
201     }
202     sensorsOwnerMatch = std::make_unique<sdbusplus::bus::match_t>(
203         bus, nameOwnerChanged(), [](auto& msg) {
204             std::string name;
205             std::string oldOwner;
206             std::string newOwner;
207             msg.read(name, oldOwner, newOwner);
208 
209             if (!name.empty() && newOwner.empty())
210             {
211                 // The service exits
212                 const auto it = serviceToIdMap.find(name);
213                 if (it == serviceToIdMap.end())
214                 {
215                     return;
216                 }
217                 for (const auto& id : it->second)
218                 {
219                     // Invalidate cache
220                     sensorCacheMap[id].reset();
221                 }
222             }
223         });
224 }
225 #endif
226 
227 // Use a lookup table to find the interface name of a specific sensor
228 // This will be used until an alternative is found.  this is the first
229 // step for mapping IPMI
find_openbmc_path(uint8_t num,dbus_interface_t * interface)230 int find_openbmc_path(uint8_t num, dbus_interface_t* interface)
231 {
232     const auto& sensor_it = ipmi::sensor::sensors.find(num);
233     if (sensor_it == ipmi::sensor::sensors.end())
234     {
235         // The sensor map does not contain the sensor requested
236         return -EINVAL;
237     }
238 
239     const auto& info = sensor_it->second;
240 
241     std::string serviceName{};
242     try
243     {
244         sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()};
245         serviceName =
246             ipmi::getService(bus, info.sensorInterface, info.sensorPath);
247     }
248     catch (const sdbusplus::exception_t&)
249     {
250         std::fprintf(stderr, "Failed to get %s busname: %s\n",
251                      info.sensorPath.c_str(), serviceName.c_str());
252         return -EINVAL;
253     }
254 
255     interface->sensortype = info.sensorType;
256     strcpy(interface->bus, serviceName.c_str());
257     strcpy(interface->path, info.sensorPath.c_str());
258     // Take the interface name from the beginning of the DbusInterfaceMap. This
259     // works for the Value interface but may not suffice for more complex
260     // sensors.
261     // tracked https://github.com/openbmc/phosphor-host-ipmid/issues/103
262     strcpy(interface->interface,
263            info.propertyInterfaces.begin()->first.c_str());
264     interface->sensornumber = num;
265 
266     return 0;
267 }
268 
269 /////////////////////////////////////////////////////////////////////
270 //
271 // Routines used by ipmi commands wanting to interact on the dbus
272 //
273 /////////////////////////////////////////////////////////////////////
set_sensor_dbus_state_s(uint8_t number,const char * method,const char * value)274 int set_sensor_dbus_state_s(uint8_t number, const char* method,
275                             const char* value)
276 {
277     dbus_interface_t a;
278     int r;
279     sd_bus_error error = SD_BUS_ERROR_NULL;
280     sd_bus_message* m = nullptr;
281 
282     r = find_openbmc_path(number, &a);
283 
284     if (r < 0)
285     {
286         std::fprintf(stderr, "Failed to find Sensor 0x%02x\n", number);
287         return 0;
288     }
289 
290     r = sd_bus_message_new_method_call(bus, &m, a.bus, a.path, a.interface,
291                                        method);
292     if (r < 0)
293     {
294         std::fprintf(stderr, "Failed to create a method call: %s",
295                      strerror(-r));
296         goto final;
297     }
298 
299     r = sd_bus_message_append(m, "v", "s", value);
300     if (r < 0)
301     {
302         std::fprintf(stderr, "Failed to create a input parameter: %s",
303                      strerror(-r));
304         goto final;
305     }
306 
307     r = sd_bus_call(bus, m, 0, &error, nullptr);
308     if (r < 0)
309     {
310         std::fprintf(stderr, "Failed to call the method: %s", strerror(-r));
311     }
312 
313 final:
314     sd_bus_error_free(&error);
315     m = sd_bus_message_unref(m);
316 
317     return 0;
318 }
set_sensor_dbus_state_y(uint8_t number,const char * method,const uint8_t value)319 int set_sensor_dbus_state_y(uint8_t number, const char* method,
320                             const uint8_t value)
321 {
322     dbus_interface_t a;
323     int r;
324     sd_bus_error error = SD_BUS_ERROR_NULL;
325     sd_bus_message* m = nullptr;
326 
327     r = find_openbmc_path(number, &a);
328 
329     if (r < 0)
330     {
331         std::fprintf(stderr, "Failed to find Sensor 0x%02x\n", number);
332         return 0;
333     }
334 
335     r = sd_bus_message_new_method_call(bus, &m, a.bus, a.path, a.interface,
336                                        method);
337     if (r < 0)
338     {
339         std::fprintf(stderr, "Failed to create a method call: %s",
340                      strerror(-r));
341         goto final;
342     }
343 
344     r = sd_bus_message_append(m, "v", "i", value);
345     if (r < 0)
346     {
347         std::fprintf(stderr, "Failed to create a input parameter: %s",
348                      strerror(-r));
349         goto final;
350     }
351 
352     r = sd_bus_call(bus, m, 0, &error, nullptr);
353     if (r < 0)
354     {
355         std::fprintf(stderr, "12 Failed to call the method: %s", strerror(-r));
356     }
357 
358 final:
359     sd_bus_error_free(&error);
360     m = sd_bus_message_unref(m);
361 
362     return 0;
363 }
364 
dbus_to_sensor_type(char * p)365 uint8_t dbus_to_sensor_type(char* p)
366 {
367     sensorTypemap_t* s = g_SensorTypeMap;
368     char r = 0;
369     while (s->number != 0xFF)
370     {
371         if (!strcmp(s->dbusname, p))
372         {
373             r = s->typecode;
374             break;
375         }
376         s++;
377     }
378 
379     if (s->number == 0xFF)
380         printf("Failed to find Sensor Type %s\n", p);
381 
382     return r;
383 }
384 
get_type_from_interface(dbus_interface_t dbus_if)385 uint8_t get_type_from_interface(dbus_interface_t dbus_if)
386 {
387     uint8_t type;
388 
389     // This is where sensors that do not exist in dbus but do
390     // exist in the host code stop.  This should indicate it
391     // is not a supported sensor
392     if (dbus_if.interface[0] == 0)
393     {
394         return 0;
395     }
396 
397     // Fetch type from interface itself.
398     if (dbus_if.sensortype != 0)
399     {
400         type = dbus_if.sensortype;
401     }
402     else
403     {
404         // Non InventoryItems
405         char* p = strrchr(dbus_if.path, '/');
406         type = dbus_to_sensor_type(p + 1);
407     }
408 
409     return type;
410 }
411 
412 // Replaces find_sensor
find_type_for_sensor_number(uint8_t num)413 uint8_t find_type_for_sensor_number(uint8_t num)
414 {
415     int r;
416     dbus_interface_t dbus_if;
417     r = find_openbmc_path(num, &dbus_if);
418     if (r < 0)
419     {
420         std::fprintf(stderr, "Could not find sensor %d\n", num);
421         return 0;
422     }
423     return get_type_from_interface(dbus_if);
424 }
425 
426 /**
427  *  @brief implements the get sensor type command.
428  *  @param - sensorNumber
429  *
430  *  @return IPMI completion code plus response data on success.
431  *   - sensorType
432  *   - eventType
433  **/
434 
435 ipmi::RspType<uint8_t, // sensorType
436               uint8_t  // eventType
437               >
ipmiGetSensorType(uint8_t sensorNumber)438     ipmiGetSensorType(uint8_t sensorNumber)
439 {
440     const auto it = ipmi::sensor::sensors.find(sensorNumber);
441     if (it == ipmi::sensor::sensors.end())
442     {
443         // The sensor map does not contain the sensor requested
444         return ipmi::responseSensorInvalid();
445     }
446 
447     const auto& info = it->second;
448     uint8_t sensorType = info.sensorType;
449     uint8_t eventType = info.sensorReadingType;
450 
451     return ipmi::responseSuccess(sensorType, eventType);
452 }
453 
454 const std::set<std::string> analogSensorInterfaces = {
455     SensorValue::interface,
456     "xyz.openbmc_project.Control.FanPwm",
457 };
458 
isAnalogSensor(const std::string & interface)459 bool isAnalogSensor(const std::string& interface)
460 {
461     return (analogSensorInterfaces.count(interface));
462 }
463 
464 /**
465 @brief This command is used to set sensorReading.
466 
467 @param
468     -  sensorNumber
469     -  operation
470     -  reading
471     -  assertOffset0_7
472     -  assertOffset8_14
473     -  deassertOffset0_7
474     -  deassertOffset8_14
475     -  eventData1
476     -  eventData2
477     -  eventData3
478 
479 @return completion code on success.
480 **/
481 
ipmiSetSensorReading(uint8_t sensorNumber,uint8_t operation,uint8_t reading,uint8_t assertOffset0_7,uint8_t assertOffset8_14,uint8_t deassertOffset0_7,uint8_t deassertOffset8_14,uint8_t eventData1,uint8_t eventData2,uint8_t eventData3)482 ipmi::RspType<> ipmiSetSensorReading(
483     uint8_t sensorNumber, uint8_t operation, uint8_t reading,
484     uint8_t assertOffset0_7, uint8_t assertOffset8_14,
485     uint8_t deassertOffset0_7, uint8_t deassertOffset8_14, uint8_t eventData1,
486     uint8_t eventData2, uint8_t eventData3)
487 {
488     lg2::debug("IPMI SET_SENSOR, sensorNumber: {SENSOR_NUM}", "SENSOR_NUM",
489                lg2::hex, sensorNumber);
490 
491     if (sensorNumber == 0xFF)
492     {
493         return ipmi::responseInvalidFieldRequest();
494     }
495     ipmi::sensor::SetSensorReadingReq cmdData;
496 
497     cmdData.number = sensorNumber;
498     cmdData.operation = operation;
499     cmdData.reading = reading;
500     cmdData.assertOffset0_7 = assertOffset0_7;
501     cmdData.assertOffset8_14 = assertOffset8_14;
502     cmdData.deassertOffset0_7 = deassertOffset0_7;
503     cmdData.deassertOffset8_14 = deassertOffset8_14;
504     cmdData.eventData1 = eventData1;
505     cmdData.eventData2 = eventData2;
506     cmdData.eventData3 = eventData3;
507 
508     // Check if the Sensor Number is present
509     const auto iter = ipmi::sensor::sensors.find(sensorNumber);
510     if (iter == ipmi::sensor::sensors.end())
511     {
512         updateSensorRecordFromSSRAESC(&sensorNumber);
513         return ipmi::responseSuccess();
514     }
515 
516     try
517     {
518         if (ipmi::sensor::Mutability::Write !=
519             (iter->second.mutability & ipmi::sensor::Mutability::Write))
520         {
521             lg2::error("Sensor Set operation is not allowed, "
522                        "sensorNumber: {SENSOR_NUM}",
523                        "SENSOR_NUM", lg2::hex, sensorNumber);
524             return ipmi::responseIllegalCommand();
525         }
526         auto ipmiRC = iter->second.updateFunc(cmdData, iter->second);
527         return ipmi::response(ipmiRC);
528     }
529     catch (const InternalFailure& e)
530     {
531         lg2::error("Set sensor failed, sensorNumber: {SENSOR_NUM}",
532                    "SENSOR_NUM", lg2::hex, sensorNumber);
533         commit<InternalFailure>();
534         return ipmi::responseUnspecifiedError();
535     }
536     catch (const std::runtime_error& e)
537     {
538         lg2::error("runtime error: {ERROR}", "ERROR", e);
539         return ipmi::responseUnspecifiedError();
540     }
541 }
542 
543 /** @brief implements the get sensor reading command
544  *  @param sensorNum - sensor number
545  *
546  *  @returns IPMI completion code plus response data
547  *   - senReading           - sensor reading
548  *   - reserved
549  *   - readState            - sensor reading state enabled
550  *   - senScanState         - sensor scan state disabled
551  *   - allEventMessageState - all Event message state disabled
552  *   - assertionStatesLsb   - threshold levels states
553  *   - assertionStatesMsb   - discrete reading sensor states
554  */
555 ipmi::RspType<uint8_t, // sensor reading
556 
557               uint5_t, // reserved
558               bool,    // reading state
559               bool,    // 0 = sensor scanning state disabled
560               bool,    // 0 = all event messages disabled
561 
562               uint8_t, // threshold levels states
563               uint8_t  // discrete reading sensor states
564               >
ipmiSensorGetSensorReading(ipmi::Context::ptr & ctx,uint8_t sensorNum)565     ipmiSensorGetSensorReading([[maybe_unused]] ipmi::Context::ptr& ctx,
566                                uint8_t sensorNum)
567 {
568     if (sensorNum == 0xFF)
569     {
570         return ipmi::responseInvalidFieldRequest();
571     }
572 
573     const auto iter = ipmi::sensor::sensors.find(sensorNum);
574     if (iter == ipmi::sensor::sensors.end())
575     {
576         return ipmi::responseSensorInvalid();
577     }
578     if (ipmi::sensor::Mutability::Read !=
579         (iter->second.mutability & ipmi::sensor::Mutability::Read))
580     {
581         return ipmi::responseIllegalCommand();
582     }
583 
584     try
585     {
586 #ifdef FEATURE_SENSORS_CACHE
587         auto& sensorData = sensorCacheMap[sensorNum];
588         if (!sensorData.has_value())
589         {
590             // No cached value, try read it
591             std::string service;
592             boost::system::error_code ec;
593             const auto& sensorInfo = iter->second;
594             ec = ipmi::getService(ctx, sensorInfo.sensorInterface,
595                                   sensorInfo.sensorPath, service);
596             if (ec)
597             {
598                 return ipmi::responseUnspecifiedError();
599             }
600             fillSensorIdServiceMap(sensorInfo.sensorPath,
601                                    sensorInfo.propertyInterfaces.begin()->first,
602                                    iter->first, service);
603 
604             ipmi::PropertyMap props;
605             ec = ipmi::getAllDbusProperties(
606                 ctx, service, sensorInfo.sensorPath,
607                 sensorInfo.propertyInterfaces.begin()->first, props);
608             if (ec)
609             {
610                 fprintf(stderr, "Failed to get sensor %s, %d: %s\n",
611                         sensorInfo.sensorPath.c_str(), ec.value(),
612                         ec.message().c_str());
613                 // Intitilizing with default values
614                 constexpr uint8_t senReading = 0;
615                 constexpr uint5_t reserved{0};
616                 constexpr bool readState = true;
617                 constexpr bool senScanState = false;
618                 constexpr bool allEventMessageState = false;
619                 constexpr uint8_t assertionStatesLsb = 0;
620                 constexpr uint8_t assertionStatesMsb = 0;
621 
622                 return ipmi::responseSuccess(
623                     senReading, reserved, readState, senScanState,
624                     allEventMessageState, assertionStatesLsb,
625                     assertionStatesMsb);
626             }
627             sensorInfo.getFunc(sensorNum, sensorInfo, props);
628         }
629         return ipmi::responseSuccess(
630             sensorData->response.reading, uint5_t(0),
631             sensorData->response.readingOrStateUnavailable,
632             sensorData->response.scanningEnabled,
633             sensorData->response.allEventMessagesEnabled,
634             sensorData->response.thresholdLevelsStates,
635             sensorData->response.discreteReadingSensorStates);
636 
637 #else
638         ipmi::sensor::GetSensorResponse getResponse =
639             iter->second.getFunc(iter->second);
640 
641         return ipmi::responseSuccess(
642             getResponse.reading, uint5_t(0),
643             getResponse.readingOrStateUnavailable, getResponse.scanningEnabled,
644             getResponse.allEventMessagesEnabled,
645             getResponse.thresholdLevelsStates,
646             getResponse.discreteReadingSensorStates);
647 #endif
648     }
649 #ifdef UPDATE_FUNCTIONAL_ON_FAIL
650     catch (const SensorFunctionalError& e)
651     {
652         return ipmi::responseResponseError();
653     }
654 #endif
655     catch (const std::exception& e)
656     {
657         // Intitilizing with default values
658         constexpr uint8_t senReading = 0;
659         constexpr uint5_t reserved{0};
660         constexpr bool readState = true;
661         constexpr bool senScanState = false;
662         constexpr bool allEventMessageState = false;
663         constexpr uint8_t assertionStatesLsb = 0;
664         constexpr uint8_t assertionStatesMsb = 0;
665 
666         return ipmi::responseSuccess(senReading, reserved, readState,
667                                      senScanState, allEventMessageState,
668                                      assertionStatesLsb, assertionStatesMsb);
669     }
670 }
671 
updateWarningThreshold(uint8_t lowerValue,uint8_t upperValue,get_sdr::GetSensorThresholdsResponse & resp)672 void updateWarningThreshold(uint8_t lowerValue, uint8_t upperValue,
673                             get_sdr::GetSensorThresholdsResponse& resp)
674 {
675     resp.lowerNonCritical = lowerValue;
676     resp.upperNonCritical = upperValue;
677     if (lowerValue)
678     {
679         resp.validMask |= static_cast<uint8_t>(
680             ipmi::sensor::ThresholdMask::NON_CRITICAL_LOW_MASK);
681     }
682 
683     if (upperValue)
684     {
685         resp.validMask |= static_cast<uint8_t>(
686             ipmi::sensor::ThresholdMask::NON_CRITICAL_HIGH_MASK);
687     }
688 }
689 
updateCriticalThreshold(uint8_t lowerValue,uint8_t upperValue,get_sdr::GetSensorThresholdsResponse & resp)690 void updateCriticalThreshold(uint8_t lowerValue, uint8_t upperValue,
691                              get_sdr::GetSensorThresholdsResponse& resp)
692 {
693     resp.lowerCritical = lowerValue;
694     resp.upperCritical = upperValue;
695     if (lowerValue)
696     {
697         resp.validMask |= static_cast<uint8_t>(
698             ipmi::sensor::ThresholdMask::CRITICAL_LOW_MASK);
699     }
700 
701     if (upperValue)
702     {
703         resp.validMask |= static_cast<uint8_t>(
704             ipmi::sensor::ThresholdMask::CRITICAL_HIGH_MASK);
705     }
706 }
707 
updateNonRecoverableThreshold(uint8_t lowerValue,uint8_t upperValue,get_sdr::GetSensorThresholdsResponse & resp)708 void updateNonRecoverableThreshold(uint8_t lowerValue, uint8_t upperValue,
709                                    get_sdr::GetSensorThresholdsResponse& resp)
710 {
711     resp.lowerNonRecoverable = lowerValue;
712     resp.upperNonRecoverable = upperValue;
713     if (lowerValue)
714     {
715         resp.validMask |= static_cast<uint8_t>(
716             ipmi::sensor::ThresholdMask::NON_RECOVERABLE_LOW_MASK);
717     }
718 
719     if (upperValue)
720     {
721         resp.validMask |= static_cast<uint8_t>(
722             ipmi::sensor::ThresholdMask::NON_RECOVERABLE_HIGH_MASK);
723     }
724 }
725 
getSensorThresholds(ipmi::Context::ptr & ctx,uint8_t sensorNum)726 get_sdr::GetSensorThresholdsResponse getSensorThresholds(
727     ipmi::Context::ptr& ctx, uint8_t sensorNum)
728 {
729     get_sdr::GetSensorThresholdsResponse resp{};
730     const auto iter = ipmi::sensor::sensors.find(sensorNum);
731     const auto info = iter->second;
732 
733     std::string service;
734     boost::system::error_code ec;
735     ec = ipmi::getService(ctx, info.sensorInterface, info.sensorPath, service);
736     if (ec)
737     {
738         return resp;
739     }
740 
741     int32_t minClamp;
742     int32_t maxClamp;
743     int32_t rawData;
744     constexpr uint8_t sensorUnitsSignedBits = 2 << 6;
745     constexpr uint8_t signedDataFormat = 0x80;
746     if ((info.sensorUnits1 & sensorUnitsSignedBits) == signedDataFormat)
747     {
748         minClamp = std::numeric_limits<int8_t>::lowest();
749         maxClamp = std::numeric_limits<int8_t>::max();
750     }
751     else
752     {
753         minClamp = std::numeric_limits<uint8_t>::lowest();
754         maxClamp = std::numeric_limits<uint8_t>::max();
755     }
756 
757     static std::vector<std::string> thresholdNames{"Warning", "Critical",
758                                                    "NonRecoverable"};
759 
760     for (const auto& thresholdName : thresholdNames)
761     {
762         std::string thresholdInterface =
763             "xyz.openbmc_project.Sensor.Threshold." + thresholdName;
764         std::string thresholdLow = thresholdName + "Low";
765         std::string thresholdHigh = thresholdName + "High";
766 
767         ipmi::PropertyMap thresholds;
768         ec = ipmi::getAllDbusProperties(ctx, service, info.sensorPath,
769                                         thresholdInterface, thresholds);
770         if (ec)
771         {
772             continue;
773         }
774 
775         double lowValue = ipmi::mappedVariant<double>(
776             thresholds, thresholdLow, std::numeric_limits<double>::quiet_NaN());
777         double highValue = ipmi::mappedVariant<double>(
778             thresholds, thresholdHigh,
779             std::numeric_limits<double>::quiet_NaN());
780 
781         uint8_t lowerValue = 0;
782         uint8_t upperValue = 0;
783         if (std::isfinite(lowValue))
784         {
785             lowValue *= std::pow(10, info.scale - info.exponentR);
786             rawData = round((lowValue - info.scaledOffset) / info.coefficientM);
787             lowerValue =
788                 static_cast<uint8_t>(std::clamp(rawData, minClamp, maxClamp));
789         }
790 
791         if (std::isfinite(highValue))
792         {
793             highValue *= std::pow(10, info.scale - info.exponentR);
794             rawData =
795                 round((highValue - info.scaledOffset) / info.coefficientM);
796             upperValue =
797                 static_cast<uint8_t>(std::clamp(rawData, minClamp, maxClamp));
798         }
799 
800         if (thresholdName == "Warning")
801         {
802             updateWarningThreshold(lowerValue, upperValue, resp);
803         }
804         else if (thresholdName == "Critical")
805         {
806             updateCriticalThreshold(lowerValue, upperValue, resp);
807         }
808         else if (thresholdName == "NonRecoverable")
809         {
810             updateNonRecoverableThreshold(lowerValue, upperValue, resp);
811         }
812     }
813 
814     return resp;
815 }
816 
817 /** @brief implements the get sensor thresholds command
818  *  @param ctx - IPMI context pointer
819  *  @param sensorNum - sensor number
820  *
821  *  @returns IPMI completion code plus response data
822  *   - validMask - threshold mask
823  *   - lower non-critical threshold - IPMI messaging state
824  *   - lower critical threshold - link authentication state
825  *   - lower non-recoverable threshold - callback state
826  *   - upper non-critical threshold
827  *   - upper critical
828  *   - upper non-recoverable
829  */
830 ipmi::RspType<uint8_t, // validMask
831               uint8_t, // lowerNonCritical
832               uint8_t, // lowerCritical
833               uint8_t, // lowerNonRecoverable
834               uint8_t, // upperNonCritical
835               uint8_t, // upperCritical
836               uint8_t  // upperNonRecoverable
837               >
ipmiSensorGetSensorThresholds(ipmi::Context::ptr & ctx,uint8_t sensorNum)838     ipmiSensorGetSensorThresholds(ipmi::Context::ptr& ctx, uint8_t sensorNum)
839 {
840     const auto iter = ipmi::sensor::sensors.find(sensorNum);
841     if (iter == ipmi::sensor::sensors.end())
842     {
843         return ipmi::responseSensorInvalid();
844     }
845 
846     const auto info = iter->second;
847 
848     // Proceed only if the sensor value interface is implemented.
849     if (info.propertyInterfaces.find(SensorValue::interface) ==
850         info.propertyInterfaces.end())
851     {
852         // return with valid mask as 0
853         return ipmi::responseSuccess();
854     }
855 
856     auto it = sensorThresholdMap.find(sensorNum);
857     if (it == sensorThresholdMap.end())
858     {
859         auto resp = getSensorThresholds(ctx, sensorNum);
860         if (resp.validMask == 0)
861         {
862             return ipmi::responseSensorInvalid();
863         }
864         sensorThresholdMap[sensorNum] = std::move(resp);
865     }
866 
867     const auto& resp = sensorThresholdMap[sensorNum];
868 
869     return ipmi::responseSuccess(
870         resp.validMask, resp.lowerNonCritical, resp.lowerCritical,
871         resp.lowerNonRecoverable, resp.upperNonCritical, resp.upperCritical,
872         resp.upperNonRecoverable);
873 }
874 
875 /** @brief implements the Set Sensor threshold command
876  *  @param sensorNumber        - sensor number
877  *  @param lowerNonCriticalThreshMask
878  *  @param lowerCriticalThreshMask
879  *  @param lowerNonRecovThreshMask
880  *  @param upperNonCriticalThreshMask
881  *  @param upperCriticalThreshMask
882  *  @param upperNonRecovThreshMask
883  *  @param reserved
884  *  @param lowerNonCritical    - lower non-critical threshold
885  *  @param lowerCritical       - Lower critical threshold
886  *  @param lowerNonRecoverable - Lower non recovarable threshold
887  *  @param upperNonCritical    - Upper non-critical threshold
888  *  @param upperCritical       - Upper critical
889  *  @param upperNonRecoverable - Upper Non-recoverable
890  *
891  *  @returns IPMI completion code
892  */
ipmiSenSetSensorThresholds(ipmi::Context::ptr & ctx,uint8_t sensorNum,bool lowerNonCriticalThreshMask,bool lowerCriticalThreshMask,bool lowerNonRecovThreshMask,bool upperNonCriticalThreshMask,bool upperCriticalThreshMask,bool upperNonRecovThreshMask,uint2_t reserved,uint8_t lowerNonCritical,uint8_t lowerCritical,uint8_t,uint8_t upperNonCritical,uint8_t upperCritical,uint8_t)893 ipmi::RspType<> ipmiSenSetSensorThresholds(
894     ipmi::Context::ptr& ctx, uint8_t sensorNum, bool lowerNonCriticalThreshMask,
895     bool lowerCriticalThreshMask, bool lowerNonRecovThreshMask,
896     bool upperNonCriticalThreshMask, bool upperCriticalThreshMask,
897     bool upperNonRecovThreshMask, uint2_t reserved, uint8_t lowerNonCritical,
898     uint8_t lowerCritical, uint8_t, uint8_t upperNonCritical,
899     uint8_t upperCritical, uint8_t)
900 {
901     if (reserved)
902     {
903         return ipmi::responseInvalidFieldRequest();
904     }
905 
906     // lower nc and upper nc not suppported on any sensor
907     if (lowerNonRecovThreshMask || upperNonRecovThreshMask)
908     {
909         return ipmi::responseInvalidFieldRequest();
910     }
911 
912     // if none of the threshold mask are set, nothing to do
913     if (!(lowerNonCriticalThreshMask | lowerCriticalThreshMask |
914           lowerNonRecovThreshMask | upperNonCriticalThreshMask |
915           upperCriticalThreshMask | upperNonRecovThreshMask))
916     {
917         return ipmi::responseSuccess();
918     }
919 
920     const auto iter = ipmi::sensor::sensors.find(sensorNum);
921     if (iter == ipmi::sensor::sensors.end())
922     {
923         return ipmi::responseSensorInvalid();
924     }
925 
926     const auto& info = iter->second;
927 
928     // Proceed only if the sensor value interface is implemented.
929     if (info.propertyInterfaces.find(SensorValue::interface) ==
930         info.propertyInterfaces.end())
931     {
932         // return with valid mask as 0
933         return ipmi::responseSuccess();
934     }
935 
936     std::string service;
937     boost::system::error_code ec;
938     ec = ipmi::getService(ctx, info.sensorInterface, info.sensorPath, service);
939     if (ec)
940     {
941         return ipmi::responseResponseError();
942     }
943     // store a vector of property name, value to set, and interface
944     std::vector<std::tuple<std::string, uint8_t, std::string>> thresholdsToSet;
945 
946     // define the indexes of the tuple
947     constexpr uint8_t propertyName = 0;
948     constexpr uint8_t thresholdValue = 1;
949     constexpr uint8_t interface = 2;
950     // verifiy all needed fields are present
951     if (lowerCriticalThreshMask || upperCriticalThreshMask)
952     {
953         ipmi::PropertyMap findThreshold;
954         ec = ipmi::getAllDbusProperties(ctx, service, info.sensorPath,
955                                         SensorThresholdCritical::interface,
956                                         findThreshold);
957 
958         if (!ec)
959         {
960             if (lowerCriticalThreshMask)
961             {
962                 auto findLower = findThreshold.find(
963                     SensorThresholdCritical::property_names::critical_low);
964                 if (findLower == findThreshold.end())
965                 {
966                     return ipmi::responseInvalidFieldRequest();
967                 }
968                 thresholdsToSet.emplace_back(
969                     SensorThresholdCritical::property_names::critical_low,
970                     lowerCritical, SensorThresholdCritical::interface);
971             }
972             if (upperCriticalThreshMask)
973             {
974                 auto findUpper = findThreshold.find(
975                     SensorThresholdCritical::property_names::critical_high);
976                 if (findUpper == findThreshold.end())
977                 {
978                     return ipmi::responseInvalidFieldRequest();
979                 }
980                 thresholdsToSet.emplace_back(
981                     SensorThresholdCritical::property_names::critical_high,
982                     upperCritical, SensorThresholdCritical::interface);
983             }
984         }
985     }
986     if (lowerNonCriticalThreshMask || upperNonCriticalThreshMask)
987     {
988         ipmi::PropertyMap findThreshold;
989         ec = ipmi::getAllDbusProperties(ctx, service, info.sensorPath,
990                                         SensorThresholdWarning::interface,
991                                         findThreshold);
992 
993         if (!ec)
994         {
995             if (lowerNonCriticalThreshMask)
996             {
997                 auto findLower = findThreshold.find(
998                     SensorThresholdWarning::property_names::warning_low);
999                 if (findLower == findThreshold.end())
1000                 {
1001                     return ipmi::responseInvalidFieldRequest();
1002                 }
1003                 thresholdsToSet.emplace_back(
1004                     SensorThresholdWarning::property_names::warning_low,
1005                     lowerNonCritical, SensorThresholdWarning::interface);
1006             }
1007             if (upperNonCriticalThreshMask)
1008             {
1009                 auto findUpper = findThreshold.find(
1010                     SensorThresholdWarning::property_names::warning_high);
1011                 if (findUpper == findThreshold.end())
1012                 {
1013                     return ipmi::responseInvalidFieldRequest();
1014                 }
1015                 thresholdsToSet.emplace_back(
1016                     SensorThresholdWarning::property_names::warning_high,
1017                     upperNonCritical, SensorThresholdWarning::interface);
1018             }
1019         }
1020     }
1021     for (const auto& property : thresholdsToSet)
1022     {
1023         // from section 36.3 in the IPMI Spec, assume all linear
1024         double valueToSet =
1025             ((info.coefficientM * std::get<thresholdValue>(property)) +
1026              (info.scaledOffset * std::pow(10.0, info.scale))) *
1027             std::pow(10.0, info.exponentR);
1028         ipmi::setDbusProperty(
1029             ctx, service, info.sensorPath, std::get<interface>(property),
1030             std::get<propertyName>(property), ipmi::Value(valueToSet));
1031     }
1032 
1033     // Invalidate the cache
1034     sensorThresholdMap.erase(sensorNum);
1035     return ipmi::responseSuccess();
1036 }
1037 
1038 /** @brief implements the get SDR Info command
1039  *  @param count - Operation
1040  *
1041  *  @returns IPMI completion code plus response data
1042  *   - sdrCount - sensor/SDR count
1043  *   - lunsAndDynamicPopulation - static/Dynamic sensor population flag
1044  */
1045 ipmi::RspType<uint8_t, // respcount
1046               uint8_t  // dynamic population flags
1047               >
ipmiSensorGetDeviceSdrInfo(std::optional<uint8_t> count)1048     ipmiSensorGetDeviceSdrInfo(std::optional<uint8_t> count)
1049 {
1050     uint8_t sdrCount;
1051     // multiple LUNs not supported.
1052     constexpr uint8_t lunsAndDynamicPopulation = 1;
1053     constexpr uint8_t getSdrCount = 0x01;
1054     constexpr uint8_t getSensorCount = 0x00;
1055 
1056     if (count.value_or(0) == getSdrCount)
1057     {
1058         // Get SDR count. This returns the total number of SDRs in the device.
1059         const auto& entityRecords =
1060             ipmi::sensor::EntityInfoMapContainer::getContainer()
1061                 ->getIpmiEntityRecords();
1062         sdrCount = ipmi::sensor::sensors.size() + frus.size() +
1063                    entityRecords.size();
1064     }
1065     else if (count.value_or(0) == getSensorCount)
1066     {
1067         // Get Sensor count. This returns the number of sensors
1068         sdrCount = ipmi::sensor::sensors.size();
1069     }
1070     else
1071     {
1072         return ipmi::responseInvalidCommandOnLun();
1073     }
1074 
1075     return ipmi::responseSuccess(sdrCount, lunsAndDynamicPopulation);
1076 }
1077 
1078 /** @brief implements the reserve SDR command
1079  *  @returns IPMI completion code plus response data
1080  *   - reservationID - reservation ID
1081  */
ipmiSensorReserveSdr()1082 ipmi::RspType<uint16_t> ipmiSensorReserveSdr()
1083 {
1084     // A constant reservation ID is okay until we implement add/remove SDR.
1085     constexpr uint16_t reservationID = 1;
1086 
1087     return ipmi::responseSuccess(reservationID);
1088 }
1089 
setUnitFieldsForObject(const ipmi::sensor::Info & info,get_sdr::SensorDataFullRecordBody & body)1090 void setUnitFieldsForObject(const ipmi::sensor::Info& info,
1091                             get_sdr::SensorDataFullRecordBody& body)
1092 {
1093     namespace server = sdbusplus::server::xyz::openbmc_project::sensor;
1094     body.sensorUnits1 = info.sensorUnits1; // default is 0. unsigned, no rate,
1095                                            // no modifier, not a %
1096     try
1097     {
1098         auto unit = server::Value::convertUnitFromString(info.unit);
1099         // Unit strings defined in
1100         // phosphor-dbus-interfaces/xyz/openbmc_project/Sensor/Value.interface.yaml
1101         switch (unit)
1102         {
1103             case server::Value::Unit::DegreesC:
1104                 body.sensorUnits2Base = get_sdr::SENSOR_UNIT_DEGREES_C;
1105                 break;
1106             case server::Value::Unit::RPMS:
1107                 body.sensorUnits2Base = get_sdr::SENSOR_UNIT_RPM;
1108                 break;
1109             case server::Value::Unit::Volts:
1110                 body.sensorUnits2Base = get_sdr::SENSOR_UNIT_VOLTS;
1111                 break;
1112             case server::Value::Unit::Meters:
1113                 body.sensorUnits2Base = get_sdr::SENSOR_UNIT_METERS;
1114                 break;
1115             case server::Value::Unit::Amperes:
1116                 body.sensorUnits2Base = get_sdr::SENSOR_UNIT_AMPERES;
1117                 break;
1118             case server::Value::Unit::Joules:
1119                 body.sensorUnits2Base = get_sdr::SENSOR_UNIT_JOULES;
1120                 break;
1121             case server::Value::Unit::Watts:
1122                 body.sensorUnits2Base = get_sdr::SENSOR_UNIT_WATTS;
1123                 break;
1124             case server::Value::Unit::Percent:
1125                 get_sdr::body::setPercentage(body);
1126                 break;
1127             default:
1128                 // Cannot be hit.
1129                 std::fprintf(stderr, "Unknown value unit type: = %s\n",
1130                              info.unit.c_str());
1131         }
1132     }
1133     catch (const sdbusplus::exception::InvalidEnumString& e)
1134     {
1135         lg2::warning("Warning: no unit provided for sensor!");
1136     }
1137 }
1138 
populateRecordFromDbus(const ipmi::sensor::Info & info,get_sdr::SensorDataFullRecordBody & body)1139 ipmi::Cc populateRecordFromDbus(const ipmi::sensor::Info& info,
1140                                 get_sdr::SensorDataFullRecordBody& body)
1141 {
1142     /* Functional sensor case */
1143     if (isAnalogSensor(info.propertyInterfaces.begin()->first))
1144     {
1145         /* Unit info */
1146         setUnitFieldsForObject(info, body);
1147 
1148         get_sdr::body::setB(info.coefficientB, body);
1149         get_sdr::body::setM(info.coefficientM, body);
1150         get_sdr::body::setBexp(info.exponentB, body);
1151         get_sdr::body::setRexp(info.exponentR, body);
1152     }
1153 
1154     /* ID string */
1155     auto idString = info.sensorName;
1156 
1157     if (idString.empty())
1158     {
1159         idString = info.sensorNameFunc(info);
1160     }
1161 
1162     if (idString.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
1163     {
1164         get_sdr::body::setIdStrLen(FULL_RECORD_ID_STR_MAX_LENGTH, body);
1165     }
1166     else
1167     {
1168         get_sdr::body::setIdStrLen(idString.length(), body);
1169     }
1170     get_sdr::body::setIdType(3, body); // "8-bit ASCII + Latin 1"
1171     strncpy(body.idString, idString.c_str(), get_sdr::body::getIdStrLen(body));
1172 
1173     return ipmi::ccSuccess;
1174 };
1175 
1176 ipmi::RspType<uint16_t,            // nextRecordId
1177               std::vector<uint8_t> // recordData
1178               >
ipmiFruGetSdr(uint16_t recordID,uint8_t offset,uint8_t bytesToRead)1179     ipmiFruGetSdr(uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
1180 {
1181     auto fru = frus.begin();
1182     uint8_t fruID{};
1183 
1184     fruID = recordID - FRU_RECORD_ID_START;
1185     fru = frus.find(fruID);
1186     if (fru == frus.end())
1187     {
1188         return ipmi::responseSensorInvalid();
1189     }
1190 
1191     get_sdr::SensorDataFruRecord record{};
1192     /* Header */
1193     record.header.recordId = recordID;
1194     record.header.sdrVersion = SDR_VERSION; // Based on IPMI Spec v2.0 rev 1.1
1195     record.header.recordType = get_sdr::SENSOR_DATA_FRU_RECORD;
1196     record.header.recordLength = sizeof(record.key) + sizeof(record.body);
1197 
1198     /* Key */
1199     record.key.fruID = fruID;
1200     record.key.accessLun |= IPMI_LOGICAL_FRU;
1201     record.key.deviceAddress = BMCTargetAddress;
1202 
1203     /* Body */
1204     record.body.entityID = fru->second[0].entityID;
1205     record.body.entityInstance = fru->second[0].entityInstance;
1206     record.body.deviceType = fruInventoryDevice;
1207     record.body.deviceTypeModifier = IPMIFruInventory;
1208 
1209     /* Device ID string */
1210     auto deviceID =
1211         fru->second[0].path.substr(fru->second[0].path.find_last_of('/') + 1,
1212                                    fru->second[0].path.length());
1213 
1214     if (deviceID.length() > get_sdr::FRU_RECORD_DEVICE_ID_MAX_LENGTH)
1215     {
1216         get_sdr::body::setDeviceIdStrLen(
1217             get_sdr::FRU_RECORD_DEVICE_ID_MAX_LENGTH, record.body);
1218     }
1219     else
1220     {
1221         get_sdr::body::setDeviceIdStrLen(deviceID.length(), record.body);
1222     }
1223 
1224     uint16_t nextRecordId{};
1225     strncpy(record.body.deviceID, deviceID.c_str(),
1226             get_sdr::body::getDeviceIdStrLen(record.body));
1227 
1228     if (++fru == frus.end())
1229     {
1230         // we have reached till end of fru, so assign the next record id to
1231         // 512(Max fru ID = 511) + Entity Record ID(may start with 0).
1232         const auto& entityRecords =
1233             ipmi::sensor::EntityInfoMapContainer::getContainer()
1234                 ->getIpmiEntityRecords();
1235         nextRecordId =
1236             (entityRecords.size())
1237                 ? entityRecords.begin()->first + ENTITY_RECORD_ID_START
1238                 : END_OF_RECORD;
1239     }
1240     else
1241     {
1242         nextRecordId = FRU_RECORD_ID_START + fru->first;
1243     }
1244 
1245     // Check for invalid offset size
1246     if (offset > sizeof(record))
1247     {
1248         return ipmi::responseParmOutOfRange();
1249     }
1250 
1251     size_t dataLen =
1252         std::min(static_cast<size_t>(bytesToRead), sizeof(record) - offset);
1253 
1254     std::vector<uint8_t> recordData(dataLen);
1255     std::memcpy(recordData.data(),
1256                 reinterpret_cast<const uint8_t*>(&record) + offset, dataLen);
1257 
1258     return ipmi::responseSuccess(nextRecordId, recordData);
1259 }
1260 
1261 ipmi::RspType<uint16_t,            // nextRecordId
1262               std::vector<uint8_t> // recordData
1263               >
ipmiEntityGetSdr(uint16_t recordID,uint8_t offset,uint8_t bytesToRead)1264     ipmiEntityGetSdr(uint16_t recordID, uint8_t offset, uint8_t bytesToRead)
1265 {
1266     const auto& entityRecords =
1267         ipmi::sensor::EntityInfoMapContainer::getContainer()
1268             ->getIpmiEntityRecords();
1269     auto entity = entityRecords.begin();
1270     uint8_t entityRecordID;
1271 
1272     entityRecordID = recordID - ENTITY_RECORD_ID_START;
1273     entity = entityRecords.find(entityRecordID);
1274     if (entity == entityRecords.end())
1275     {
1276         return ipmi::responseSensorInvalid();
1277     }
1278 
1279     get_sdr::SensorDataEntityRecord record{};
1280     /* Header */
1281     record.header.recordId = recordID;
1282     record.header.sdrVersion = SDR_VERSION; // Based on IPMI Spec v2.0 rev 1.1
1283     record.header.recordType = get_sdr::SENSOR_DATA_ENTITY_RECORD;
1284     record.header.recordLength = sizeof(record.key) + sizeof(record.body);
1285 
1286     /* Key */
1287     record.key.containerEntityId = entity->second.containerEntityId;
1288     record.key.containerEntityInstance = entity->second.containerEntityInstance;
1289     get_sdr::key::setFlags(entity->second.isList, entity->second.isLinked,
1290                            record.key);
1291     record.key.entityId1 = entity->second.containedEntities[0].first;
1292     record.key.entityInstance1 = entity->second.containedEntities[0].second;
1293 
1294     /* Body */
1295     record.body.entityId2 = entity->second.containedEntities[1].first;
1296     record.body.entityInstance2 = entity->second.containedEntities[1].second;
1297     record.body.entityId3 = entity->second.containedEntities[2].first;
1298     record.body.entityInstance3 = entity->second.containedEntities[2].second;
1299     record.body.entityId4 = entity->second.containedEntities[3].first;
1300     record.body.entityInstance4 = entity->second.containedEntities[3].second;
1301 
1302     uint16_t nextRecordId{};
1303     if (++entity == entityRecords.end())
1304     {
1305         nextRecordId = END_OF_RECORD;
1306     }
1307     else
1308     {
1309         nextRecordId = entity->first + ENTITY_RECORD_ID_START;
1310     }
1311 
1312     // Check for invalid offset size
1313     if (offset > sizeof(record))
1314     {
1315         return ipmi::responseParmOutOfRange();
1316     }
1317 
1318     size_t dataLen =
1319         std::min(static_cast<size_t>(bytesToRead), sizeof(record) - offset);
1320 
1321     std::vector<uint8_t> recordData(dataLen);
1322     std::memcpy(recordData.data(),
1323                 reinterpret_cast<const uint8_t*>(&record) + offset, dataLen);
1324 
1325     return ipmi::responseSuccess(nextRecordId, recordData);
1326 }
1327 
1328 ipmi::RspType<uint16_t,            // nextRecordId
1329               std::vector<uint8_t> // recordData
1330               >
ipmiSensorGetSdr(uint16_t,uint16_t recordID,uint8_t offset,uint8_t bytesToRead)1331     ipmiSensorGetSdr(uint16_t /* reservationId */, uint16_t recordID,
1332                      uint8_t offset, uint8_t bytesToRead)
1333 {
1334     // Note: we use an iterator so we can provide the next ID at the end of
1335     // the call.
1336     auto sensor = ipmi::sensor::sensors.begin();
1337 
1338     // At the beginning of a scan, the host side will send us id=0.
1339     if (recordID != 0)
1340     {
1341         // recordID 0 to 255 means it is a FULL record.
1342         // recordID 256 to 511 means it is a FRU record.
1343         // recordID greater then 511 means it is a Entity Association
1344         // record. Currently we are supporting three record types: FULL
1345         // record, FRU record and Enttiy Association record.
1346         if (recordID >= ENTITY_RECORD_ID_START)
1347         {
1348             return ipmiEntityGetSdr(recordID, offset, bytesToRead);
1349         }
1350         else if (recordID >= FRU_RECORD_ID_START &&
1351                  recordID < ENTITY_RECORD_ID_START)
1352         {
1353             return ipmiFruGetSdr(recordID, offset, bytesToRead);
1354         }
1355         else
1356         {
1357             sensor = ipmi::sensor::sensors.find(recordID);
1358             if (sensor == ipmi::sensor::sensors.end())
1359             {
1360                 return ipmi::responseSensorInvalid();
1361             }
1362         }
1363     }
1364 
1365     uint8_t sensorId = sensor->first;
1366 
1367     auto it = sdrCacheMap.find(sensorId);
1368     if (it == sdrCacheMap.end())
1369     {
1370         /* Header */
1371         get_sdr::SensorDataFullRecord record = {};
1372         record.header.recordId = sensorId;
1373         record.header.sdrVersion = 0x51; // Based on IPMI Spec v2.0 rev 1.1
1374         record.header.recordType = get_sdr::SENSOR_DATA_FULL_RECORD;
1375         record.header.recordLength = sizeof(record.key) + sizeof(record.body);
1376 
1377         /* Key */
1378         get_sdr::key::setOwnerIdBmc(record.key);
1379         record.key.sensorNumber = sensorId;
1380 
1381         /* Body */
1382         record.body.entityId = sensor->second.entityType;
1383         record.body.sensorType = sensor->second.sensorType;
1384         record.body.eventReadingType = sensor->second.sensorReadingType;
1385         record.body.entityInstance = sensor->second.instance;
1386         if (ipmi::sensor::Mutability::Write ==
1387             (sensor->second.mutability & ipmi::sensor::Mutability::Write))
1388         {
1389             get_sdr::body::initSettableState(true, record.body);
1390         }
1391 
1392         // Set the type-specific details given the DBus interface
1393         populateRecordFromDbus(sensor->second, record.body);
1394         sdrCacheMap[sensorId] = std::move(record);
1395     }
1396 
1397     uint16_t nextRecordId{};
1398     const auto& record = sdrCacheMap[sensorId];
1399 
1400     if (++sensor == ipmi::sensor::sensors.end())
1401     {
1402         // we have reached till end of sensor, so assign the next record id
1403         // to 256(Max Sensor ID = 255) + FRU ID(may start with 0).
1404         nextRecordId = (frus.size()) ? frus.begin()->first + FRU_RECORD_ID_START
1405                                      : END_OF_RECORD;
1406     }
1407     else
1408     {
1409         nextRecordId = sensor->first;
1410     }
1411 
1412     if (offset > sizeof(record))
1413     {
1414         return ipmi::responseParmOutOfRange();
1415     }
1416 
1417     size_t dataLen =
1418         std::min(static_cast<size_t>(bytesToRead), sizeof(record) - offset);
1419 
1420     std::vector<uint8_t> recordData(dataLen);
1421     std::memcpy(recordData.data(),
1422                 reinterpret_cast<const uint8_t*>(&record) + offset, dataLen);
1423 
1424     return ipmi::responseSuccess(nextRecordId, recordData);
1425 }
1426 
isFromSystemChannel()1427 static bool isFromSystemChannel()
1428 {
1429     // TODO we could not figure out where the request is from based on IPMI
1430     // command handler parameters. because of it, we can not differentiate
1431     // request from SMS/SMM or IPMB channel
1432     return true;
1433 }
1434 
ipmicmdPlatformEvent(ipmi::Context::ptr & ctx,const std::vector<uint8_t> & data)1435 ipmi::RspType<> ipmicmdPlatformEvent(ipmi::Context::ptr& ctx,
1436                                      const std::vector<uint8_t>& data)
1437 {
1438     size_t paraLen = data.size();
1439     if (paraLen < selSystemEventSizeWith1Bytes ||
1440         paraLen > selSystemEventSizeWith3Bytes)
1441     {
1442         return ipmi::responseReqDataLenInvalid();
1443     }
1444 
1445     uint16_t generatorID = 0xff;
1446     std::string sensorPath = "IPMB";
1447     const uint8_t* raw = data.data();
1448     const PlatformEventRequest* req = nullptr;
1449     if (isFromSystemChannel())
1450     {
1451         // first byte for SYSTEM Interface is Generator ID +1 to get common
1452         // struct
1453         req = reinterpret_cast<const PlatformEventRequest*>(raw + 1);
1454         // Capture the generator ID
1455         generatorID = *raw;
1456         // Platform Event usually comes from other firmware, like BIOS.
1457         // Unlike BMC sensor, it does not have BMC DBUS sensor path.
1458         sensorPath = "System";
1459     }
1460     else
1461     {
1462         req = reinterpret_cast<const PlatformEventRequest*>(raw);
1463         // TODO GenratorID for IPMB is combination of RqSA and RqLUN
1464     }
1465 
1466     // Content of event data field depends on sensor class.
1467     // When data0 bit[5:4] is non-zero, valid data counts is 3.
1468     // When data0 bit[7:6] is non-zero, valid data counts is 2.
1469     if (((req->data[0] & byte3EnableMask) != 0 &&
1470          paraLen < selSystemEventSizeWith3Bytes) ||
1471         ((req->data[0] & byte2EnableMask) != 0 &&
1472          paraLen < selSystemEventSizeWith2Bytes))
1473     {
1474         return ipmi::responseReqDataLenInvalid();
1475     }
1476 
1477     // Count bytes of Event Data
1478     size_t count;
1479     if ((req->data[0] & byte3EnableMask) != 0)
1480     {
1481         count = 3;
1482     }
1483     else if ((req->data[0] & byte2EnableMask) != 0)
1484     {
1485         count = 2;
1486     }
1487     else
1488     {
1489         count = 1;
1490     }
1491     bool assert = req->eventDirectionType & directionMask ? false : true;
1492     std::vector<uint8_t> eventData(req->data, req->data + count);
1493 
1494     auto ec = ipmi::callDbusMethod(
1495         ctx, ipmiSELObject, ipmiSELPath, ipmiSELAddInterface, "IpmiSelAdd",
1496         ipmiSELAddMessage, sensorPath, eventData, assert, generatorID);
1497     if (ec)
1498     {
1499         lg2::error("IpmiSelAdd call failed: {ERROR}", "ERROR", ec.message());
1500         return ipmi::responseUnspecifiedError();
1501     }
1502 
1503     return ipmi::responseSuccess();
1504 }
1505 
registerNetFnSenFunctions()1506 void registerNetFnSenFunctions()
1507 {
1508     // Handlers with dbus-sdr handler implementation.
1509     // Do not register the hander if it dynamic sensors stack is used.
1510 
1511 #ifndef FEATURE_DYNAMIC_SENSORS
1512 
1513 #ifdef FEATURE_SENSORS_CACHE
1514     // Initialize the sensor matches
1515     initSensorMatches();
1516 #endif
1517 
1518     // <Set Sensor Reading and Event Status>
1519     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1520                           ipmi::sensor_event::cmdSetSensorReadingAndEvtSts,
1521                           ipmi::Privilege::Operator, ipmiSetSensorReading);
1522     // <Get Sensor Reading>
1523     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1524                           ipmi::sensor_event::cmdGetSensorReading,
1525                           ipmi::Privilege::User, ipmiSensorGetSensorReading);
1526 
1527     // <Reserve Device SDR Repository>
1528     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1529                           ipmi::sensor_event::cmdReserveDeviceSdrRepository,
1530                           ipmi::Privilege::User, ipmiSensorReserveSdr);
1531 
1532     // <Get Device SDR Info>
1533     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1534                           ipmi::sensor_event::cmdGetDeviceSdrInfo,
1535                           ipmi::Privilege::User, ipmiSensorGetDeviceSdrInfo);
1536 
1537     // <Get Sensor Thresholds>
1538     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1539                           ipmi::sensor_event::cmdGetSensorThreshold,
1540                           ipmi::Privilege::User, ipmiSensorGetSensorThresholds);
1541 
1542     // <Set Sensor Thresholds>
1543     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1544                           ipmi::sensor_event::cmdSetSensorThreshold,
1545                           ipmi::Privilege::User, ipmiSenSetSensorThresholds);
1546 
1547     // <Get Device SDR>
1548     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1549                           ipmi::sensor_event::cmdGetDeviceSdr,
1550                           ipmi::Privilege::User, ipmiSensorGetSdr);
1551 
1552 #endif
1553 
1554     // Common Handers used by both implementation.
1555 
1556     // <Platform Event Message>
1557     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1558                           ipmi::sensor_event::cmdPlatformEvent,
1559                           ipmi::Privilege::Operator, ipmicmdPlatformEvent);
1560 
1561     // <Get Sensor Type>
1562     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnSensor,
1563                           ipmi::sensor_event::cmdGetSensorType,
1564                           ipmi::Privilege::User, ipmiGetSensorType);
1565 
1566     return;
1567 }
1568