1 #include <mapper.h>
2 #include <math.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <bitset>
6 #include <xyz/openbmc_project/Sensor/Value/server.hpp>
7 #include <systemd/sd-bus.h>
8 #include "host-ipmid/ipmid-api.h"
9 #include <phosphor-logging/log.hpp>
10 #include <phosphor-logging/elog-errors.hpp>
11 #include "ipmid.hpp"
12 #include "sensorhandler.h"
13 #include "types.hpp"
14 #include "utils.hpp"
15 #include "xyz/openbmc_project/Common/error.hpp"
16 
17 
18 extern int updateSensorRecordFromSSRAESC(const void *);
19 extern sd_bus *bus;
20 extern const ipmi::sensor::IdInfoMap sensors;
21 using namespace phosphor::logging;
22 using InternalFailure =
23     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
24 
25 void register_netfn_sen_functions()   __attribute__((constructor));
26 
27 struct sensorTypemap_t {
28     uint8_t number;
29     uint8_t typecode;
30     char dbusname[32];
31 } ;
32 
33 sensorTypemap_t g_SensorTypeMap[] = {
34 
35     {0x01, 0x6F, "Temp"},
36     {0x0C, 0x6F, "DIMM"},
37     {0x0C, 0x6F, "MEMORY_BUFFER"},
38     {0x07, 0x6F, "PROC"},
39     {0x07, 0x6F, "CORE"},
40     {0x07, 0x6F, "CPU"},
41     {0x0F, 0x6F, "BootProgress"},
42     {0xe9, 0x09, "OccStatus"},  // E9 is an internal mapping to handle sensor type code os 0x09
43     {0xC3, 0x6F, "BootCount"},
44     {0x1F, 0x6F, "OperatingSystemStatus"},
45     {0x12, 0x6F, "SYSTEM_EVENT"},
46     {0xC7, 0x03, "SYSTEM"},
47     {0xC7, 0x03, "MAIN_PLANAR"},
48     {0xC2, 0x6F, "PowerCap"},
49     {0xD8, 0x03, "PowerSupplyRedundancy"},
50     {0xDA, 0x03, "TurboAllowed"},
51     {0xB4, 0x6F, "PowerSupplyDerating"},
52     {0xFF, 0x00, ""},
53 };
54 
55 
56 struct sensor_data_t {
57     uint8_t sennum;
58 }  __attribute__ ((packed)) ;
59 
60 struct sensorreadingresp_t {
61     uint8_t value;
62     uint8_t operation;
63     uint8_t indication[2];
64 }  __attribute__ ((packed)) ;
65 
66 // Use a lookup table to find the interface name of a specific sensor
67 // This will be used until an alternative is found.  this is the first
68 // step for mapping IPMI
69 int find_interface_property_fru_type(dbus_interface_t *interface, const char *property_name, char *property_value) {
70 
71     char  *str1;
72     sd_bus_error error = SD_BUS_ERROR_NULL;
73     sd_bus_message *reply = NULL, *m=NULL;
74 
75 
76     int r;
77 
78     r = sd_bus_message_new_method_call(bus,&m,interface->bus,interface->path,"org.freedesktop.DBus.Properties","Get");
79     if (r < 0) {
80         fprintf(stderr, "Failed to create a method call: %s", strerror(-r));
81         fprintf(stderr,"Bus: %s Path: %s Interface: %s \n",
82                 interface->bus, interface->path, interface->interface);
83         goto final;
84     }
85 
86     r = sd_bus_message_append(m, "ss", "org.openbmc.InventoryItem", property_name);
87     if (r < 0) {
88         fprintf(stderr, "Failed to create a input parameter: %s", strerror(-r));
89         fprintf(stderr,"Bus: %s Path: %s Interface: %s \n",
90                 interface->bus, interface->path, interface->interface);
91         goto final;
92     }
93 
94     r = sd_bus_call(bus, m, 0, &error, &reply);
95     if (r < 0) {
96         fprintf(stderr, "Failed to call the method: %s", strerror(-r));
97         goto final;
98     }
99 
100     r = sd_bus_message_read(reply, "v",  "s", &str1) ;
101     if (r < 0) {
102         fprintf(stderr, "Failed to get a response: %s", strerror(-r));
103         goto final;
104     }
105 
106     strcpy(property_value, str1);
107 
108 final:
109 
110     sd_bus_error_free(&error);
111     m = sd_bus_message_unref(m);
112     reply = sd_bus_message_unref(reply);
113 
114     return r;
115 }
116 
117 int get_bus_for_path(const char *path, char **busname) {
118     return mapper_get_service(bus, path, busname);
119 }
120 
121 int legacy_dbus_openbmc_path(const char *type, const uint8_t num, dbus_interface_t *interface) {
122     char  *busname = NULL;
123     const char  *iface = "org.openbmc.managers.System";
124     const char  *objname = "/org/openbmc/managers/System";
125     char  *str1 = NULL, *str2, *str3;
126     sd_bus_error error = SD_BUS_ERROR_NULL;
127     sd_bus_message *reply = NULL;
128 
129 
130     int r;
131     r = get_bus_for_path(objname, &busname);
132     if (r < 0) {
133         fprintf(stderr, "Failed to get %s busname: %s\n",
134                 objname, strerror(-r));
135         goto final;
136     }
137 
138     r = sd_bus_call_method(bus,busname,objname,iface, "getObjectFromByteId",
139                            &error, &reply, "sy", type, num);
140     if (r < 0) {
141         fprintf(stderr, "Failed to create a method call: %s", strerror(-r));
142         goto final;
143     }
144 
145     r = sd_bus_message_read(reply, "(ss)", &str2, &str3);
146     if (r < 0) {
147         fprintf(stderr, "Failed to get a response: %s", strerror(-r));
148         goto final;
149     }
150 
151     r = get_bus_for_path(str2, &str1);
152     if (r < 0) {
153         fprintf(stderr, "Failed to get %s busname: %s\n",
154                 str2, strerror(-r));
155         goto final;
156     }
157 
158     strncpy(interface->bus, str1, MAX_DBUS_PATH);
159     strncpy(interface->path, str2, MAX_DBUS_PATH);
160     strncpy(interface->interface, str3, MAX_DBUS_PATH);
161 
162     interface->sensornumber = num;
163     // Make sure we know that the type hasn't been set, as newer codebase will
164     // set it automatically from the YAML at this step.
165     interface->sensortype = 0;
166 
167 final:
168 
169     sd_bus_error_free(&error);
170     reply = sd_bus_message_unref(reply);
171     free(busname);
172     free(str1);
173 
174     return r;
175 }
176 
177 // Use a lookup table to find the interface name of a specific sensor
178 // This will be used until an alternative is found.  this is the first
179 // step for mapping IPMI
180 int find_openbmc_path(uint8_t num, dbus_interface_t *interface) {
181     int rc;
182 
183     // When the sensor map does not contain the sensor requested,
184     // fall back to the legacy DBus lookup (deprecated)
185     const auto& sensor_it = sensors.find(num);
186     if (sensor_it == sensors.end())
187     {
188         return legacy_dbus_openbmc_path("SENSOR", num, interface);
189     }
190 
191     const auto& info = sensor_it->second;
192 
193     char* busname = nullptr;
194     rc = get_bus_for_path(info.sensorPath.c_str(), &busname);
195     if (rc < 0) {
196         fprintf(stderr, "Failed to get %s busname: %s\n",
197                 info.sensorPath.c_str(),
198                 busname);
199         goto final;
200     }
201 
202     interface->sensortype = info.sensorType;
203     strcpy(interface->bus, busname);
204     strcpy(interface->path, info.sensorPath.c_str());
205     // Take the interface name from the beginning of the DbusInterfaceMap. This
206     // works for the Value interface but may not suffice for more complex
207     // sensors.
208     // tracked https://github.com/openbmc/phosphor-host-ipmid/issues/103
209     strcpy(interface->interface, info.sensorInterfaces.begin()->first.c_str());
210     interface->sensornumber = num;
211 
212 final:
213     free(busname);
214     return rc;
215 }
216 
217 
218 /////////////////////////////////////////////////////////////////////
219 //
220 // Routines used by ipmi commands wanting to interact on the dbus
221 //
222 /////////////////////////////////////////////////////////////////////
223 int set_sensor_dbus_state_s(uint8_t number, const char *method, const char *value) {
224 
225 
226     dbus_interface_t a;
227     int r;
228     sd_bus_error error = SD_BUS_ERROR_NULL;
229     sd_bus_message *m=NULL;
230 
231     fprintf(ipmidbus, "Attempting to set a dbus Variant Sensor 0x%02x via %s with a value of %s\n",
232         number, method, value);
233 
234     r = find_openbmc_path(number, &a);
235 
236     if (r < 0) {
237         fprintf(stderr, "Failed to find Sensor 0x%02x\n", number);
238         return 0;
239     }
240 
241     r = sd_bus_message_new_method_call(bus,&m,a.bus,a.path,a.interface,method);
242     if (r < 0) {
243         fprintf(stderr, "Failed to create a method call: %s", strerror(-r));
244         goto final;
245     }
246 
247     r = sd_bus_message_append(m, "v", "s", value);
248     if (r < 0) {
249         fprintf(stderr, "Failed to create a input parameter: %s", strerror(-r));
250         goto final;
251     }
252 
253 
254     r = sd_bus_call(bus, m, 0, &error, NULL);
255     if (r < 0) {
256         fprintf(stderr, "Failed to call the method: %s", strerror(-r));
257     }
258 
259 final:
260     sd_bus_error_free(&error);
261     m = sd_bus_message_unref(m);
262 
263     return 0;
264 }
265 int set_sensor_dbus_state_y(uint8_t number, const char *method, const uint8_t value) {
266 
267 
268     dbus_interface_t a;
269     int r;
270     sd_bus_error error = SD_BUS_ERROR_NULL;
271     sd_bus_message *m=NULL;
272 
273     fprintf(ipmidbus, "Attempting to set a dbus Variant Sensor 0x%02x via %s with a value of 0x%02x\n",
274         number, method, value);
275 
276     r = find_openbmc_path(number, &a);
277 
278     if (r < 0) {
279         fprintf(stderr, "Failed to find Sensor 0x%02x\n", number);
280         return 0;
281     }
282 
283     r = sd_bus_message_new_method_call(bus,&m,a.bus,a.path,a.interface,method);
284     if (r < 0) {
285         fprintf(stderr, "Failed to create a method call: %s", strerror(-r));
286         goto final;
287     }
288 
289     r = sd_bus_message_append(m, "v", "i", value);
290     if (r < 0) {
291         fprintf(stderr, "Failed to create a input parameter: %s", strerror(-r));
292         goto final;
293     }
294 
295 
296     r = sd_bus_call(bus, m, 0, &error, NULL);
297     if (r < 0) {
298         fprintf(stderr, "12 Failed to call the method: %s", strerror(-r));
299     }
300 
301 final:
302     sd_bus_error_free(&error);
303     m = sd_bus_message_unref(m);
304 
305     return 0;
306 }
307 
308 uint8_t dbus_to_sensor_type(char *p) {
309 
310     sensorTypemap_t *s = g_SensorTypeMap;
311     char r=0;
312 
313     while (s->number != 0xFF) {
314         if (!strcmp(s->dbusname,p)) {
315             r = s->number;
316              break;
317         }
318         s++;
319     }
320 
321 
322     if (s->number == 0xFF)
323         printf("Failed to find Sensor Type %s\n", p);
324 
325     return r;
326 }
327 
328 
329 uint8_t dbus_to_sensor_type_from_dbus(dbus_interface_t *a) {
330     char fru_type_name[64];
331     int r= 0;
332 
333     r = find_interface_property_fru_type(a, "fru_type", fru_type_name);
334     if (r<0) {
335         fprintf(stderr, "Failed to get a fru type: %s", strerror(-r));
336         return -1;
337     } else {
338         return dbus_to_sensor_type(fru_type_name);
339     }
340 }
341 
342 uint8_t get_type_from_interface(dbus_interface_t dbus_if) {
343 
344     char *p;
345     uint8_t type;
346 
347     // This is where sensors that do not exist in dbus but do
348     // exist in the host code stop.  This should indicate it
349     // is not a supported sensor
350     if (dbus_if.interface[0] == 0) { return 0;}
351 
352     // Fetch type from interface itself.
353     if (dbus_if.sensortype != 0)
354     {
355         type = dbus_if.sensortype;
356     }
357     // Legacy codebase does not populate type during initial handling:
358     else if (strstr(dbus_if.interface, "InventoryItem")) {
359         // InventoryItems are real frus.  So need to get the
360         // fru_type property
361         type = dbus_to_sensor_type_from_dbus(&dbus_if);
362     } else {
363         // Non InventoryItems
364         p = strrchr (dbus_if.path, '/');
365         type = dbus_to_sensor_type(p+1);
366     }
367 
368     return type;
369  }
370 
371 // Replaces find_sensor
372 uint8_t find_type_for_sensor_number(uint8_t num) {
373     int r;
374     dbus_interface_t dbus_if;
375     r = find_openbmc_path(num, &dbus_if);
376     if (r < 0) {
377         fprintf(stderr, "Could not find sensor %d\n", num);
378         return r;
379     }
380     return get_type_from_interface(dbus_if);
381 }
382 
383 
384 
385 
386 
387 ipmi_ret_t ipmi_sen_get_sensor_type(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
388                              ipmi_request_t request, ipmi_response_t response,
389                              ipmi_data_len_t data_len, ipmi_context_t context)
390 {
391     sensor_data_t *reqptr = (sensor_data_t*)request;
392     ipmi_ret_t rc = IPMI_CC_OK;
393 
394     printf("IPMI GET_SENSOR_TYPE [0x%02X]\n",reqptr->sennum);
395 
396     // TODO Not sure what the System-event-sensor is suppose to return
397     // need to ask Hostboot team
398     unsigned char buf[] = {0x00,0x6F};
399 
400     buf[0] = find_type_for_sensor_number(reqptr->sennum);
401 
402     // HACK UNTIL Dbus gets updated or we find a better way
403     if (buf[0] == 0) {
404         rc = IPMI_CC_SENSOR_INVALID;
405     }
406 
407 
408     *data_len = sizeof(buf);
409     memcpy(response, &buf, *data_len);
410 
411     return rc;
412 }
413 
414 ipmi_ret_t setSensorReading(void *request)
415 {
416     SetSensorReadingReq cmdData =
417             *(static_cast<SetSensorReadingReq *>(request));
418 
419     // Check if the Sensor Number is present
420     const auto iter = sensors.find(cmdData.number);
421     if (iter == sensors.end())
422     {
423         return IPMI_CC_SENSOR_INVALID;
424     }
425 
426     try
427     {
428         return iter->second.updateFunc(cmdData, iter->second);
429     }
430     catch (InternalFailure& e)
431     {
432          log<level::ERR>("Set sensor failed",
433                          entry("SENSOR_NUM=%d", cmdData.number));
434          commit<InternalFailure>();
435     }
436 
437     return IPMI_CC_UNSPECIFIED_ERROR;
438 }
439 
440 ipmi_ret_t ipmi_sen_set_sensor(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
441                              ipmi_request_t request, ipmi_response_t response,
442                              ipmi_data_len_t data_len, ipmi_context_t context)
443 {
444     sensor_data_t *reqptr = (sensor_data_t*)request;
445 
446     printf("IPMI SET_SENSOR [0x%02x]\n",reqptr->sennum);
447 
448     /*
449      * This would support the Set Sensor Reading command for the presence
450      * and functional state of Processor, Core & DIMM. For the remaining
451      * sensors the existing support is invoked.
452      */
453     auto ipmiRC = setSensorReading(request);
454 
455     if(ipmiRC == IPMI_CC_SENSOR_INVALID)
456     {
457         updateSensorRecordFromSSRAESC(reqptr);
458         ipmiRC = IPMI_CC_OK;
459     }
460 
461     *data_len=0;
462     return ipmiRC;
463 }
464 
465 
466 ipmi_ret_t ipmi_sen_get_sensor_reading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
467                              ipmi_request_t request, ipmi_response_t response,
468                              ipmi_data_len_t data_len, ipmi_context_t context)
469 {
470     sensor_data_t *reqptr = (sensor_data_t*)request;
471     ipmi_ret_t rc = IPMI_CC_SENSOR_INVALID;
472     uint8_t type;
473     sensorreadingresp_t *resp = (sensorreadingresp_t*) response;
474     int r;
475     dbus_interface_t a;
476     sd_bus *bus = ipmid_get_sd_bus_connection();
477     sd_bus_message *reply = NULL;
478     int reading = 0;
479 
480 
481     printf("IPMI GET_SENSOR_READING [0x%02x]\n",reqptr->sennum);
482 
483     r = find_openbmc_path(reqptr->sennum, &a);
484 
485     if (r < 0) {
486         fprintf(stderr, "Failed to find Sensor 0x%02x\n", reqptr->sennum);
487         return IPMI_CC_SENSOR_INVALID;
488     }
489 
490     type = get_type_from_interface(a);
491     if(type == 0) {
492         fprintf(stderr, "Failed to find Sensor 0x%02x\n", reqptr->sennum);
493         return IPMI_CC_SENSOR_INVALID;
494     }
495 
496     fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n", a.bus, a.path, a.interface);
497 
498     *data_len=0;
499 
500     int64_t raw_value;
501     ipmi::sensor::Info sensor;
502 
503     switch(type) {
504         case 0xC3:
505         case 0xC2:
506             r = sd_bus_get_property(bus,a.bus, a.path, a.interface, "value", NULL, &reply, "i");
507             if (r < 0) {
508                 fprintf(stderr, "Failed to call sd_bus_get_property:%d,  %s\n", r, strerror(-r));
509                 fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n",
510                         a.bus, a.path, a.interface);
511                 break;
512             }
513 
514             r = sd_bus_message_read(reply, "i", &reading);
515             if (r < 0) {
516                 fprintf(stderr, "Failed to read sensor: %s\n", strerror(-r));
517                 break;
518             }
519 
520             printf("Contents of a 0x%02x is 0x%02x\n", type, reading);
521 
522             rc = IPMI_CC_OK;
523             *data_len=sizeof(sensorreadingresp_t);
524 
525             resp->value         = (uint8_t)reading;
526             resp->operation     = 0;
527             resp->indication[0] = 0;
528             resp->indication[1] = 0;
529             break;
530 
531         case IPMI_SENSOR_TEMP:
532         case IPMI_SENSOR_VOLTAGE:
533         case IPMI_SENSOR_CURRENT:
534         case IPMI_SENSOR_FAN:
535             // Get reading for /xyz/openbmc_project/Sensor/Value.interface
536             if(sensors.find(reqptr->sennum) == sensors.end())
537             {
538                 fprintf(stderr, "Failed to find config entry for Sensor 0x%02x\n",
539                         reqptr->sennum);
540                 return IPMI_CC_SENSOR_INVALID;
541             }
542 
543             sensor = sensors.at(reqptr->sennum);
544 
545             // Get value
546             r = sd_bus_get_property_trivial(bus,
547                                             a.bus,
548                                             a.path,
549                                             a.interface,
550                                             "Value",
551                                             NULL,
552                                             'x',
553                                             &raw_value);
554             if (r < 0) {
555                 fprintf(stderr,
556                         "Failed to call sd_bus_get_property:%d,  %s, 'value'\n",
557                         r,
558                         strerror(-r));
559                 fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n",
560                         a.bus, a.path, a.interface);
561                 break;
562             }
563 
564             // Prevent div0
565             if (sensor.coefficientM == 0) {
566                 sensor.coefficientM = 1;
567             };
568 
569             resp->value = static_cast<uint8_t>(
570                     (raw_value - sensor.scaledOffset) / sensor.coefficientM);
571             resp->operation = 1<<6; // scanning enabled
572             resp->indication[0] = 0; // not a threshold sensor. ignore
573             resp->indication[1] = 0;
574             rc = IPMI_CC_OK;
575             *data_len=sizeof(sensorreadingresp_t);
576             break;
577 
578         default:
579             *data_len=0;
580             rc = IPMI_CC_SENSOR_INVALID;
581             break;
582     }
583 
584 
585     reply = sd_bus_message_unref(reply);
586 
587     return rc;
588 }
589 
590 ipmi_ret_t ipmi_sen_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
591                              ipmi_request_t request, ipmi_response_t response,
592                              ipmi_data_len_t data_len, ipmi_context_t context)
593 {
594     ipmi_ret_t rc = IPMI_CC_INVALID;
595 
596     printf("IPMI S/E Wildcard Netfn:[0x%X], Cmd:[0x%X]\n",netfn,cmd);
597     *data_len = 0;
598 
599     return rc;
600 }
601 
602 ipmi_ret_t ipmi_sen_get_sdr_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
603                                  ipmi_request_t request,
604                                  ipmi_response_t response,
605                                  ipmi_data_len_t data_len,
606                                  ipmi_context_t context)
607 {
608     auto resp = static_cast<get_sdr_info::GetSdrInfoResp*>(response);
609     if (request == nullptr ||
610         get_sdr_info::request::get_count(request) == false)
611     {
612         // Get Sensor Count
613         resp->count = sensors.size();
614     }
615     else
616     {
617         resp->count = 1;
618     }
619 
620     // Multiple LUNs not supported.
621     namespace response = get_sdr_info::response;
622     response::set_lun_present(0, &(resp->luns_and_dynamic_population));
623     response::set_lun_not_present(1, &(resp->luns_and_dynamic_population));
624     response::set_lun_not_present(2, &(resp->luns_and_dynamic_population));
625     response::set_lun_not_present(3, &(resp->luns_and_dynamic_population));
626     response::set_static_population(&(resp->luns_and_dynamic_population));
627 
628     *data_len = SDR_INFO_RESP_SIZE;
629 
630     return IPMI_CC_OK;
631 }
632 
633 ipmi_ret_t ipmi_sen_reserve_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
634                                 ipmi_request_t request,
635                                 ipmi_response_t response,
636                                 ipmi_data_len_t data_len,
637                                 ipmi_context_t context)
638 {
639     // A constant reservation ID is okay until we implement add/remove SDR.
640     const uint16_t reservation_id = 1;
641     *(uint16_t*)response = reservation_id;
642 
643     printf("Created new IPMI SDR reservation ID %d\n", *(uint16_t*)response);
644     return IPMI_CC_OK;
645 }
646 
647 ipmi_ret_t populate_record_from_dbus(get_sdr::SensorDataFullRecordBody *body,
648                                      const ipmi::sensor::Info *info,
649                                      ipmi_data_len_t data_len)
650 {
651     /* Functional sensor case */
652     if (info->sensorInterfaces.begin()->first ==
653             "xyz.openbmc_project.Sensor.Value")
654     {
655         // Get bus
656         sd_bus *bus = ipmid_get_sd_bus_connection();
657         dbus_interface_t iface;
658 
659         if (0 > find_openbmc_path(body->entity_id, &iface))
660             return IPMI_CC_SENSOR_INVALID;
661 
662         body->sensor_units_1 = 0; // unsigned, no rate, no modifier, not a %
663 
664         /* Unit info */
665         char *raw_cstr;
666         if (0 > sd_bus_get_property_string(bus, iface.bus, iface.path,
667                                             iface.interface, "Unit", NULL,
668                                             &raw_cstr)) {
669             fprintf(stderr, "Expected to find Unit interface in bus %s, path %s, but it was missing.\n",
670                     iface.bus, iface.path);
671             return IPMI_CC_SENSOR_INVALID;
672         }
673 
674         std::string raw_str(raw_cstr);
675         namespace server = sdbusplus::xyz::openbmc_project::Sensor::server;
676         server::Value::Unit unit =
677             server::Value::convertUnitFromString(raw_str);
678 
679         // Unit strings defined in
680         // phosphor-dbus-interfaces/xyz/openbmc_project/Sensor/Value.interface.yaml
681         switch (unit)
682         {
683             case server::Value::Unit::DegreesC:
684                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_DEGREES_C;
685                 break;
686             case server::Value::Unit::RPMS:
687                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_REVOLUTIONS; // revolutions
688                 get_sdr::body::set_rate_unit(0b100, body); // per minute
689                 break;
690             case server::Value::Unit::Volts:
691                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_VOLTS;
692                 break;
693             case server::Value::Unit::Meters:
694                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_METERS;
695                 break;
696             case server::Value::Unit::Amperes:
697                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_AMPERES;
698                 break;
699             case server::Value::Unit::Joules:
700                 body->sensor_units_2_base = get_sdr::SENSOR_UNIT_JOULES;
701                 break;
702             default:
703                 fprintf(stderr, "Unknown value unit type: = %s\n", raw_cstr);
704         }
705 
706         free(raw_cstr);
707 
708         /* Modifiers to reading info */
709         // Get scale
710         int64_t scale;
711         if (0 > sd_bus_get_property_trivial(bus,
712                                         iface.bus,
713                                         iface.path,
714                                         iface.interface,
715                                         "Scale",
716                                         NULL,
717                                         'x',
718                                         &scale)) {
719             fprintf(stderr, "Expected to find Scale interface in bus %s, path %s, but it was missing.\n",
720                     iface.bus, iface.path);
721             return IPMI_CC_SENSOR_INVALID;
722         }
723 
724         get_sdr::body::set_b(info->coefficientB, body);
725         get_sdr::body::set_m(info->coefficientM, body);
726         get_sdr::body::set_b_exp(info->exponentB, body);
727         get_sdr::body::set_r_exp(scale, body);
728 
729         /* ID string */
730         std::string id_string = info->sensorPath.substr(
731             info->sensorPath.find_last_of('/')+1, info->sensorPath.length());
732         get_sdr::body::set_id_type(0b00, body); // 00 = unicode
733         if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
734         {
735             get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH, body);
736         }
737         else
738         {
739             get_sdr::body::set_id_strlen(id_string.length(), body);
740         }
741         strncpy(body->id_string, id_string.c_str(),
742                 get_sdr::body::get_id_strlen(body));
743     }
744 
745     return IPMI_CC_OK;
746 };
747 
748 ipmi_ret_t ipmi_sen_get_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
749                             ipmi_request_t request, ipmi_response_t response,
750                             ipmi_data_len_t data_len, ipmi_context_t context)
751 {
752     ipmi_ret_t ret = IPMI_CC_OK;
753     get_sdr::GetSdrReq *req = (get_sdr::GetSdrReq*)request;
754     get_sdr::GetSdrResp *resp = (get_sdr::GetSdrResp*)response;
755     get_sdr::SensorDataFullRecord record = {0};
756     if (req != NULL)
757     {
758         // Note: we use an iterator so we can provide the next ID at the end of
759         // the call.
760         auto sensor = sensors.begin();
761 
762         // At the beginning of a scan, the host side will send us id=0.
763         if (get_sdr::request::get_record_id(req) != 0)
764         {
765             sensor = sensors.find(get_sdr::request::get_record_id(req));
766             if(sensor == sensors.end()) {
767                 return IPMI_CC_SENSOR_INVALID;
768             }
769         }
770 
771         uint8_t sensor_id = sensor->first;
772 
773         /* Header */
774         get_sdr::header::set_record_id(sensor_id, &(record.header));
775         record.header.sdr_version = 0x51; // Based on IPMI Spec v2.0 rev 1.1
776         record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
777         record.header.record_length = sizeof(get_sdr::SensorDataFullRecord);
778 
779         /* Key */
780         record.key.sensor_number = sensor_id;
781 
782         /* Body */
783         record.body.entity_id = sensor_id;
784         record.body.sensor_type = sensor->second.sensorType;
785         record.body.event_reading_type = sensor->second.sensorReadingType;
786 
787         // Set the type-specific details given the DBus interface
788         ret = populate_record_from_dbus(&(record.body), &(sensor->second),
789                                         data_len);
790 
791         if (++sensor == sensors.end())
792         {
793             get_sdr::response::set_next_record_id(0xFFFF, resp); // last record
794         }
795         else
796         {
797             get_sdr::response::set_next_record_id(sensor->first, resp);
798         }
799 
800         *data_len = sizeof(get_sdr::GetSdrResp) - req->offset;
801         memcpy(resp->record_data, (char*)&record + req->offset,
802                sizeof(get_sdr::SensorDataFullRecord) - req->offset);
803     }
804 
805     return ret;
806 }
807 
808 
809 void register_netfn_sen_functions()
810 {
811     // <Wildcard Command>
812     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
813            NETFUN_SENSOR, IPMI_CMD_WILDCARD);
814     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_WILDCARD,
815                            nullptr, ipmi_sen_wildcard,
816                            PRIVILEGE_USER);
817 
818     // <Get Sensor Type>
819     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
820            NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_TYPE);
821     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_TYPE,
822                            nullptr, ipmi_sen_get_sensor_type,
823                            PRIVILEGE_USER);
824 
825     // <Set Sensor Reading and Event Status>
826     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
827            NETFUN_SENSOR, IPMI_CMD_SET_SENSOR);
828     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_SET_SENSOR,
829                            nullptr, ipmi_sen_set_sensor,
830                            PRIVILEGE_OPERATOR);
831 
832     // <Get Sensor Reading>
833     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
834            NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_READING);
835     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_READING,
836                            nullptr, ipmi_sen_get_sensor_reading,
837                            PRIVILEGE_USER);
838 
839     // <Reserve SDR>
840     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
841            NETFUN_SENSOR, IPMI_CMD_RESERVE_SDR_REPO);
842     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_RESERVE_SDR_REPO,
843                            nullptr, ipmi_sen_reserve_sdr,
844                            PRIVILEGE_USER);
845 
846     // <Get SDR Info>
847     printf("Registering NetFn:[0x%X], Cmd:[0x%x]\n",
848            NETFUN_SENSOR, IPMI_CMD_GET_SDR_INFO);
849     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SDR_INFO,
850                            nullptr, ipmi_sen_get_sdr_info,
851                            PRIVILEGE_USER);
852 
853     // <Get SDR>
854     printf("Registering NetFn:[0x%X], Cmd:[0x%x]\n",
855            NETFUN_SENSOR, IPMI_CMD_GET_SDR);
856     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SDR,
857                            nullptr, ipmi_sen_get_sdr,
858                            PRIVILEGE_USER);
859 
860     return;
861 }
862