1 #include <mapper.h>
2 #include <math.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <set>
6 #include <bitset>
7 #include <xyz/openbmc_project/Sensor/Value/server.hpp>
8 #include <systemd/sd-bus.h>
9 #include "host-ipmid/ipmid-api.h"
10 #include <phosphor-logging/log.hpp>
11 #include <phosphor-logging/elog-errors.hpp>
12 #include "ipmid.hpp"
13 #include "sensorhandler.h"
14 #include "types.hpp"
15 #include "utils.hpp"
16 #include "xyz/openbmc_project/Common/error.hpp"
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     {0x0b, 0xCA, "PowerSupplyRedundancy"},
50     {0xDA, 0x03, "TurboAllowed"},
51     {0xD8, 0xC8, "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.propertyInterfaces.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     while (s->number != 0xFF) {
313         if (!strcmp(s->dbusname,p)) {
314             r = s->typecode;
315              break;
316         }
317         s++;
318     }
319 
320     if (s->number == 0xFF)
321         printf("Failed to find Sensor Type %s\n", p);
322 
323     return r;
324 }
325 
326 
327 uint8_t dbus_to_sensor_type_from_dbus(dbus_interface_t *a) {
328     char fru_type_name[64];
329     int r= 0;
330 
331     r = find_interface_property_fru_type(a, "fru_type", fru_type_name);
332     if (r<0) {
333         fprintf(stderr, "Failed to get a fru type: %s", strerror(-r));
334         return -1;
335     } else {
336         return dbus_to_sensor_type(fru_type_name);
337     }
338 }
339 
340 uint8_t get_type_from_interface(dbus_interface_t dbus_if) {
341 
342     char *p;
343     uint8_t type;
344 
345     // This is where sensors that do not exist in dbus but do
346     // exist in the host code stop.  This should indicate it
347     // is not a supported sensor
348     if (dbus_if.interface[0] == 0) { return 0;}
349 
350     // Fetch type from interface itself.
351     if (dbus_if.sensortype != 0)
352     {
353         type = dbus_if.sensortype;
354     }
355     // Legacy codebase does not populate type during initial handling:
356     else if (strstr(dbus_if.interface, "InventoryItem")) {
357         // InventoryItems are real frus.  So need to get the
358         // fru_type property
359         type = dbus_to_sensor_type_from_dbus(&dbus_if);
360     } else {
361         // Non InventoryItems
362         p = strrchr (dbus_if.path, '/');
363         type = dbus_to_sensor_type(p+1);
364     }
365 
366     return type;
367  }
368 
369 // Replaces find_sensor
370 uint8_t find_type_for_sensor_number(uint8_t num) {
371     int r;
372     dbus_interface_t dbus_if;
373     r = find_openbmc_path(num, &dbus_if);
374     if (r < 0) {
375         fprintf(stderr, "Could not find sensor %d\n", num);
376         return r;
377     }
378     return get_type_from_interface(dbus_if);
379 }
380 
381 
382 
383 
384 
385 ipmi_ret_t ipmi_sen_get_sensor_type(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
386                              ipmi_request_t request, ipmi_response_t response,
387                              ipmi_data_len_t data_len, ipmi_context_t context)
388 {
389     sensor_data_t *reqptr = (sensor_data_t*)request;
390     ipmi_ret_t rc = IPMI_CC_OK;
391 
392     printf("IPMI GET_SENSOR_TYPE [0x%02X]\n",reqptr->sennum);
393 
394     // TODO Not sure what the System-event-sensor is suppose to return
395     // need to ask Hostboot team
396     unsigned char buf[] = {0x00,0x6F};
397 
398     buf[0] = find_type_for_sensor_number(reqptr->sennum);
399 
400     // HACK UNTIL Dbus gets updated or we find a better way
401     if (buf[0] == 0) {
402         rc = IPMI_CC_SENSOR_INVALID;
403     }
404 
405 
406     *data_len = sizeof(buf);
407     memcpy(response, &buf, *data_len);
408 
409     return rc;
410 }
411 
412 const std::set<std::string> analogSensorInterfaces =
413 {
414     "xyz.openbmc_project.Sensor.Value",
415 };
416 
417 bool isAnalogSensor(const std::string& interface)
418 {
419     return (analogSensorInterfaces.count(interface));
420 }
421 
422 ipmi_ret_t setSensorReading(void *request)
423 {
424     ipmi::sensor::SetSensorReadingReq cmdData =
425             *(static_cast<ipmi::sensor::SetSensorReadingReq *>(request));
426 
427     // Check if the Sensor Number is present
428     const auto iter = sensors.find(cmdData.number);
429     if (iter == sensors.end())
430     {
431         return IPMI_CC_SENSOR_INVALID;
432     }
433 
434     try
435     {
436         return iter->second.updateFunc(cmdData, iter->second);
437     }
438     catch (InternalFailure& e)
439     {
440          log<level::ERR>("Set sensor failed",
441                          entry("SENSOR_NUM=%d", cmdData.number));
442          commit<InternalFailure>();
443     }
444     catch (const std::runtime_error& e)
445     {
446         log<level::ERR>(e.what());
447     }
448 
449     return IPMI_CC_UNSPECIFIED_ERROR;
450 }
451 
452 ipmi_ret_t ipmi_sen_set_sensor(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
453                              ipmi_request_t request, ipmi_response_t response,
454                              ipmi_data_len_t data_len, ipmi_context_t context)
455 {
456     sensor_data_t *reqptr = (sensor_data_t*)request;
457 
458     printf("IPMI SET_SENSOR [0x%02x]\n",reqptr->sennum);
459 
460     /*
461      * This would support the Set Sensor Reading command for the presence
462      * and functional state of Processor, Core & DIMM. For the remaining
463      * sensors the existing support is invoked.
464      */
465     auto ipmiRC = setSensorReading(request);
466 
467     if(ipmiRC == IPMI_CC_SENSOR_INVALID)
468     {
469         updateSensorRecordFromSSRAESC(reqptr);
470         ipmiRC = IPMI_CC_OK;
471     }
472 
473     *data_len=0;
474     return ipmiRC;
475 }
476 
477 
478 ipmi_ret_t ipmi_sen_get_sensor_reading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
479                              ipmi_request_t request, ipmi_response_t response,
480                              ipmi_data_len_t data_len, ipmi_context_t context)
481 {
482     sensor_data_t *reqptr = (sensor_data_t*)request;
483     ipmi_ret_t rc = IPMI_CC_SENSOR_INVALID;
484     uint8_t type = 0;
485     sensorreadingresp_t *resp = (sensorreadingresp_t*) response;
486     int r;
487     dbus_interface_t a;
488     sd_bus *bus = ipmid_get_sd_bus_connection();
489     sd_bus_message *reply = NULL;
490     int reading = 0;
491 
492     printf("IPMI GET_SENSOR_READING [0x%02x]\n",reqptr->sennum);
493 
494     r = find_openbmc_path(reqptr->sennum, &a);
495 
496     if (r < 0)
497     {
498         fprintf(stderr, "Failed to find Sensor 0x%02x\n", reqptr->sennum);
499     }
500     else
501     {
502         type = get_type_from_interface(a);
503         if(type == 0) {
504             fprintf(stderr, "Failed to find Sensor 0x%02x\n", reqptr->sennum);
505             return IPMI_CC_SENSOR_INVALID;
506         }
507 
508         fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n", a.bus, a.path,
509                         a.interface);
510     }
511 
512     *data_len=0;
513 
514     int64_t raw_value;
515     ipmi::sensor::Info sensor;
516 
517     switch(type) {
518         case 0xC2:
519         case 0xC8:
520             r = sd_bus_get_property(bus,a.bus, a.path, a.interface, "value", NULL, &reply, "i");
521             if (r < 0) {
522                 fprintf(stderr, "Failed to call sd_bus_get_property:%d,  %s\n", r, strerror(-r));
523                 fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n",
524                         a.bus, a.path, a.interface);
525                 break;
526             }
527 
528             r = sd_bus_message_read(reply, "i", &reading);
529             if (r < 0) {
530                 fprintf(stderr, "Failed to read sensor: %s\n", strerror(-r));
531                 break;
532             }
533 
534             printf("Contents of a 0x%02x is 0x%02x\n", type, reading);
535 
536             rc = IPMI_CC_OK;
537             *data_len=sizeof(sensorreadingresp_t);
538 
539             resp->value         = (uint8_t)reading;
540             resp->operation     = 0;
541             resp->indication[0] = 0;
542             resp->indication[1] = 0;
543             break;
544 
545         case IPMI_SENSOR_TEMP:
546         case IPMI_SENSOR_VOLTAGE:
547         case IPMI_SENSOR_CURRENT:
548         case IPMI_SENSOR_FAN:
549             // Get reading for /xyz/openbmc_project/Sensor/Value.interface
550             if(sensors.find(reqptr->sennum) == sensors.end())
551             {
552                 fprintf(stderr, "Failed to find config entry for Sensor 0x%02x\n",
553                         reqptr->sennum);
554                 return IPMI_CC_SENSOR_INVALID;
555             }
556 
557             sensor = sensors.at(reqptr->sennum);
558             if (ipmi::sensor::Mutability::Read !=
559                   (sensor.mutability & ipmi::sensor::Mutability::Read))
560             {
561                 log<level::ERR>("Sensor was not readable.\n");
562                 return IPMI_CC_SENSOR_INVALID;
563             }
564 
565 
566             // Get value
567             r = sd_bus_get_property_trivial(bus,
568                                             a.bus,
569                                             a.path,
570                                             a.interface,
571                                             "Value",
572                                             NULL,
573                                             'x',
574                                             &raw_value);
575             if (r < 0) {
576                 fprintf(stderr,
577                         "Failed to call sd_bus_get_property:%d,  %s, 'value'\n",
578                         r,
579                         strerror(-r));
580                 fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n",
581                         a.bus, a.path, a.interface);
582                 break;
583             }
584 
585             // Prevent div0
586             if (sensor.coefficientM == 0) {
587                 sensor.coefficientM = 1;
588             };
589 
590             resp->value = static_cast<uint8_t>(
591                     (raw_value - sensor.scaledOffset) / sensor.coefficientM);
592             resp->operation = 1<<6; // scanning enabled
593             resp->indication[0] = 0; // not a threshold sensor. ignore
594             resp->indication[1] = 0;
595             rc = IPMI_CC_OK;
596             *data_len=sizeof(sensorreadingresp_t);
597             break;
598         default:
599         {
600             const auto iter = sensors.find(reqptr->sennum);
601             if (iter == sensors.end())
602             {
603                 return IPMI_CC_SENSOR_INVALID;
604             }
605 
606             try
607             {
608                 auto getResponse =  iter->second.getFunc(iter->second);
609                 *data_len = getResponse.size();
610                 memcpy(resp, getResponse.data(), *data_len);
611                 return IPMI_CC_OK;
612             }
613             catch (InternalFailure& e)
614             {
615                  log<level::ERR>("Get sensor failed",
616                                  entry("SENSOR_NUM=%d", reqptr->sennum));
617                  commit<InternalFailure>();
618                  return IPMI_CC_SENSOR_INVALID;
619             }
620             catch (const std::runtime_error& e)
621             {
622                 log<level::ERR>(e.what());
623                 return IPMI_CC_SENSOR_INVALID;
624             }
625         }
626     }
627 
628     reply = sd_bus_message_unref(reply);
629 
630     return rc;
631 }
632 
633 ipmi_ret_t ipmi_sen_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
634                              ipmi_request_t request, ipmi_response_t response,
635                              ipmi_data_len_t data_len, ipmi_context_t context)
636 {
637     ipmi_ret_t rc = IPMI_CC_INVALID;
638 
639     printf("IPMI S/E Wildcard Netfn:[0x%X], Cmd:[0x%X]\n",netfn,cmd);
640     *data_len = 0;
641 
642     return rc;
643 }
644 
645 ipmi_ret_t ipmi_sen_get_sdr_info(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
646                                  ipmi_request_t request,
647                                  ipmi_response_t response,
648                                  ipmi_data_len_t data_len,
649                                  ipmi_context_t context)
650 {
651     auto resp = static_cast<get_sdr_info::GetSdrInfoResp*>(response);
652     if (request == nullptr ||
653         get_sdr_info::request::get_count(request) == false)
654     {
655         // Get Sensor Count
656         resp->count = sensors.size();
657     }
658     else
659     {
660         resp->count = 1;
661     }
662 
663     // Multiple LUNs not supported.
664     namespace response = get_sdr_info::response;
665     response::set_lun_present(0, &(resp->luns_and_dynamic_population));
666     response::set_lun_not_present(1, &(resp->luns_and_dynamic_population));
667     response::set_lun_not_present(2, &(resp->luns_and_dynamic_population));
668     response::set_lun_not_present(3, &(resp->luns_and_dynamic_population));
669     response::set_static_population(&(resp->luns_and_dynamic_population));
670 
671     *data_len = SDR_INFO_RESP_SIZE;
672 
673     return IPMI_CC_OK;
674 }
675 
676 ipmi_ret_t ipmi_sen_reserve_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
677                                 ipmi_request_t request,
678                                 ipmi_response_t response,
679                                 ipmi_data_len_t data_len,
680                                 ipmi_context_t context)
681 {
682     // A constant reservation ID is okay until we implement add/remove SDR.
683     const uint16_t reservation_id = 1;
684     *(uint16_t*)response = reservation_id;
685     *data_len = sizeof(uint16_t);
686 
687     printf("Created new IPMI SDR reservation ID %d\n", *(uint16_t*)response);
688     return IPMI_CC_OK;
689 }
690 
691 void setUnitFieldsForObject(sd_bus *bus,
692                             const dbus_interface_t &iface,
693                             const ipmi::sensor::Info *info,
694                             get_sdr::SensorDataFullRecordBody *body)
695 {
696     if (info->propertyInterfaces.begin()->first ==
697         "xyz.openbmc_project.Sensor.Value")
698     {
699         std::string result {};
700         if (info->unit.empty())
701         {
702             char *raw_cstr = NULL;
703             if (0 > sd_bus_get_property_string(bus, iface.bus, iface.path,
704                                                iface.interface, "Unit", NULL,
705                                                &raw_cstr))
706             {
707                 log<level::WARNING>("Unit interface missing.",
708                                     entry("BUS=%s", iface.bus),
709                                     entry("PATH=%s", iface.path));
710             }
711             else
712             {
713                 result = raw_cstr;
714             }
715             free(raw_cstr);
716         }
717         else
718         {
719             result = info->unit;
720         }
721 
722         namespace server = sdbusplus::xyz::openbmc_project::Sensor::server;
723         try {
724             auto unit = server::Value::convertUnitFromString(result);
725             // Unit strings defined in
726             // phosphor-dbus-interfaces/xyz/openbmc_project/Sensor/Value.interface.yaml
727             switch (unit)
728             {
729                 case server::Value::Unit::DegreesC:
730                     body->sensor_units_2_base = get_sdr::SENSOR_UNIT_DEGREES_C;
731                     break;
732                 case server::Value::Unit::RPMS:
733                     body->sensor_units_2_base = get_sdr::SENSOR_UNIT_REVOLUTIONS; // revolutions
734                     get_sdr::body::set_rate_unit(0b100, body); // per minute
735                     break;
736                 case server::Value::Unit::Volts:
737                     body->sensor_units_2_base = get_sdr::SENSOR_UNIT_VOLTS;
738                     break;
739                 case server::Value::Unit::Meters:
740                     body->sensor_units_2_base = get_sdr::SENSOR_UNIT_METERS;
741                     break;
742                 case server::Value::Unit::Amperes:
743                     body->sensor_units_2_base = get_sdr::SENSOR_UNIT_AMPERES;
744                     break;
745                 case server::Value::Unit::Joules:
746                     body->sensor_units_2_base = get_sdr::SENSOR_UNIT_JOULES;
747                     break;
748                 default:
749                     // Cannot be hit.
750                     fprintf(stderr, "Unknown value unit type: = %s\n", result.c_str());
751             }
752         }
753         catch (sdbusplus::exception::InvalidEnumString e)
754         {
755             log<level::WARNING>("Warning: no unit provided for sensor!");
756         }
757     }
758 }
759 
760 int64_t getScaleForObject(sd_bus *bus,
761                           const dbus_interface_t& iface,
762                           const ipmi::sensor::Info *info)
763 {
764     int64_t result = 0;
765     if (info->propertyInterfaces.begin()->first ==
766         "xyz.openbmc_project.Sensor.Value")
767     {
768         if (info->hasScale)
769         {
770             result = info->scale;
771         }
772         else
773         {
774             if (0 > sd_bus_get_property_trivial(bus,
775                                                 iface.bus,
776                                                 iface.path,
777                                                 iface.interface,
778                                                 "Scale",
779                                                 NULL,
780                                                 'x',
781                                                 &result)) {
782                 log<level::WARNING>("Scale interface missing.",
783                                     entry("BUS=%s", iface.bus),
784                                     entry("PATH=%s", iface.path));
785             }
786         }
787     }
788 
789     return result;
790 }
791 
792 ipmi_ret_t populate_record_from_dbus(get_sdr::SensorDataFullRecordBody *body,
793                                      const ipmi::sensor::Info *info,
794                                      ipmi_data_len_t data_len)
795 {
796     /* Functional sensor case */
797     if (isAnalogSensor(info->propertyInterfaces.begin()->first))
798     {
799         // Get bus
800         sd_bus *bus = ipmid_get_sd_bus_connection();
801         dbus_interface_t iface;
802 
803         if (0 > find_openbmc_path(body->entity_id, &iface))
804             return IPMI_CC_SENSOR_INVALID;
805 
806         body->sensor_units_1 = 0; // unsigned, no rate, no modifier, not a %
807 
808         /* Unit info */
809         setUnitFieldsForObject(bus, iface, info, body);
810 
811         /* Modifiers to reading info */
812         // Get scale
813         int64_t scale = getScaleForObject(bus, iface, info);
814 
815         get_sdr::body::set_b(info->coefficientB, body);
816         get_sdr::body::set_m(info->coefficientM, body);
817         get_sdr::body::set_b_exp(info->exponentB, body);
818         get_sdr::body::set_r_exp(scale, body);
819 
820         /* ID string */
821         std::string id_string = info->sensorPath.substr(
822             info->sensorPath.find_last_of('/')+1, info->sensorPath.length());
823         get_sdr::body::set_id_type(0b00, body); // 00 = unicode
824         if (id_string.length() > FULL_RECORD_ID_STR_MAX_LENGTH)
825         {
826             get_sdr::body::set_id_strlen(FULL_RECORD_ID_STR_MAX_LENGTH, body);
827         }
828         else
829         {
830             get_sdr::body::set_id_strlen(id_string.length(), body);
831         }
832         strncpy(body->id_string, id_string.c_str(),
833                 get_sdr::body::get_id_strlen(body));
834     }
835 
836     return IPMI_CC_OK;
837 };
838 
839 ipmi_ret_t ipmi_sen_get_sdr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
840                             ipmi_request_t request, ipmi_response_t response,
841                             ipmi_data_len_t data_len, ipmi_context_t context)
842 {
843     ipmi_ret_t ret = IPMI_CC_OK;
844     get_sdr::GetSdrReq *req = (get_sdr::GetSdrReq*)request;
845     get_sdr::GetSdrResp *resp = (get_sdr::GetSdrResp*)response;
846     get_sdr::SensorDataFullRecord record = {0};
847     if (req != NULL)
848     {
849         // Note: we use an iterator so we can provide the next ID at the end of
850         // the call.
851         auto sensor = sensors.begin();
852 
853         // At the beginning of a scan, the host side will send us id=0.
854         if (get_sdr::request::get_record_id(req) != 0)
855         {
856             sensor = sensors.find(get_sdr::request::get_record_id(req));
857             if(sensor == sensors.end()) {
858                 return IPMI_CC_SENSOR_INVALID;
859             }
860         }
861 
862         uint8_t sensor_id = sensor->first;
863 
864         /* Header */
865         get_sdr::header::set_record_id(sensor_id, &(record.header));
866         record.header.sdr_version = 0x51; // Based on IPMI Spec v2.0 rev 1.1
867         record.header.record_type = get_sdr::SENSOR_DATA_FULL_RECORD;
868         record.header.record_length = sizeof(get_sdr::SensorDataFullRecord);
869 
870         /* Key */
871         record.key.sensor_number = sensor_id;
872 
873         /* Body */
874         record.body.entity_id = sensor_id;
875         record.body.sensor_type = sensor->second.sensorType;
876         record.body.event_reading_type = sensor->second.sensorReadingType;
877 
878         // Set the type-specific details given the DBus interface
879         ret = populate_record_from_dbus(&(record.body), &(sensor->second),
880                                         data_len);
881 
882         if (++sensor == sensors.end())
883         {
884             get_sdr::response::set_next_record_id(0xFFFF, resp); // last record
885         }
886         else
887         {
888             get_sdr::response::set_next_record_id(sensor->first, resp);
889         }
890 
891         *data_len = sizeof(get_sdr::GetSdrResp) - req->offset;
892         memcpy(resp->record_data, (char*)&record + req->offset,
893                sizeof(get_sdr::SensorDataFullRecord) - req->offset);
894     }
895 
896     return ret;
897 }
898 
899 
900 void register_netfn_sen_functions()
901 {
902     // <Wildcard Command>
903     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
904            NETFUN_SENSOR, IPMI_CMD_WILDCARD);
905     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_WILDCARD,
906                            nullptr, ipmi_sen_wildcard,
907                            PRIVILEGE_USER);
908 
909     // <Get Sensor Type>
910     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
911            NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_TYPE);
912     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_TYPE,
913                            nullptr, ipmi_sen_get_sensor_type,
914                            PRIVILEGE_USER);
915 
916     // <Set Sensor Reading and Event Status>
917     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
918            NETFUN_SENSOR, IPMI_CMD_SET_SENSOR);
919     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_SET_SENSOR,
920                            nullptr, ipmi_sen_set_sensor,
921                            PRIVILEGE_OPERATOR);
922 
923     // <Get Sensor Reading>
924     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
925            NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_READING);
926     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_READING,
927                            nullptr, ipmi_sen_get_sensor_reading,
928                            PRIVILEGE_USER);
929 
930     // <Reserve SDR>
931     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",
932            NETFUN_SENSOR, IPMI_CMD_RESERVE_SDR_REPO);
933     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_RESERVE_SDR_REPO,
934                            nullptr, ipmi_sen_reserve_sdr,
935                            PRIVILEGE_USER);
936 
937     // <Get SDR Info>
938     printf("Registering NetFn:[0x%X], Cmd:[0x%x]\n",
939            NETFUN_SENSOR, IPMI_CMD_GET_SDR_INFO);
940     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SDR_INFO,
941                            nullptr, ipmi_sen_get_sdr_info,
942                            PRIVILEGE_USER);
943 
944     // <Get SDR>
945     printf("Registering NetFn:[0x%X], Cmd:[0x%x]\n",
946            NETFUN_SENSOR, IPMI_CMD_GET_SDR);
947     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SDR,
948                            nullptr, ipmi_sen_get_sdr,
949                            PRIVILEGE_USER);
950 
951     return;
952 }
953