1 #include <mapper.h>
2 #include <math.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <bitset>
6 #include <systemd/sd-bus.h>
7 #include "host-ipmid/ipmid-api.h"
8 #include <phosphor-logging/log.hpp>
9 #include "ipmid.hpp"
10 #include "sensorhandler.h"
11 #include "types.hpp"
12 #include "utils.hpp"
13 
14 extern int updateSensorRecordFromSSRAESC(const void *);
15 extern sd_bus *bus;
16 extern const ipmi::sensor::IdInfoMap sensors;
17 using namespace phosphor::logging;
18 
19 void register_netfn_sen_functions()   __attribute__((constructor));
20 
21 struct sensorTypemap_t {
22     uint8_t number;
23     uint8_t typecode;
24     char dbusname[32];
25 } ;
26 
27 
28 sensorTypemap_t g_SensorTypeMap[] = {
29 
30     {0x01, 0x6F, "Temp"},
31     {0x0C, 0x6F, "DIMM"},
32     {0x0C, 0x6F, "MEMORY_BUFFER"},
33     {0x07, 0x6F, "PROC"},
34     {0x07, 0x6F, "CORE"},
35     {0x07, 0x6F, "CPU"},
36     {0x0F, 0x6F, "BootProgress"},
37     {0xe9, 0x09, "OccStatus"},  // E9 is an internal mapping to handle sensor type code os 0x09
38     {0xC3, 0x6F, "BootCount"},
39     {0x1F, 0x6F, "OperatingSystemStatus"},
40     {0x12, 0x6F, "SYSTEM_EVENT"},
41     {0xC7, 0x03, "SYSTEM"},
42     {0xC7, 0x03, "MAIN_PLANAR"},
43     {0xC2, 0x6F, "PowerCap"},
44     {0xD8, 0x03, "PowerSupplyRedundancy"},
45     {0xDA, 0x03, "TurboAllowed"},
46     {0xB4, 0x6F, "PowerSupplyDerating"},
47     {0xFF, 0x00, ""},
48 };
49 
50 
51 struct sensor_data_t {
52     uint8_t sennum;
53 }  __attribute__ ((packed)) ;
54 
55 struct sensorreadingresp_t {
56     uint8_t value;
57     uint8_t operation;
58     uint8_t indication[2];
59 }  __attribute__ ((packed)) ;
60 
61 // Use a lookup table to find the interface name of a specific sensor
62 // This will be used until an alternative is found.  this is the first
63 // step for mapping IPMI
64 int find_interface_property_fru_type(dbus_interface_t *interface, const char *property_name, char *property_value) {
65 
66     char  *str1;
67     sd_bus_error error = SD_BUS_ERROR_NULL;
68     sd_bus_message *reply = NULL, *m=NULL;
69 
70 
71     int r;
72 
73     r = sd_bus_message_new_method_call(bus,&m,interface->bus,interface->path,"org.freedesktop.DBus.Properties","Get");
74     if (r < 0) {
75         fprintf(stderr, "Failed to create a method call: %s", strerror(-r));
76         fprintf(stderr,"Bus: %s Path: %s Interface: %s \n",
77                 interface->bus, interface->path, interface->interface);
78         goto final;
79     }
80 
81     r = sd_bus_message_append(m, "ss", "org.openbmc.InventoryItem", property_name);
82     if (r < 0) {
83         fprintf(stderr, "Failed to create a input parameter: %s", strerror(-r));
84         fprintf(stderr,"Bus: %s Path: %s Interface: %s \n",
85                 interface->bus, interface->path, interface->interface);
86         goto final;
87     }
88 
89     r = sd_bus_call(bus, m, 0, &error, &reply);
90     if (r < 0) {
91         fprintf(stderr, "Failed to call the method: %s", strerror(-r));
92         goto final;
93     }
94 
95     r = sd_bus_message_read(reply, "v",  "s", &str1) ;
96     if (r < 0) {
97         fprintf(stderr, "Failed to get a response: %s", strerror(-r));
98         goto final;
99     }
100 
101     strcpy(property_value, str1);
102 
103 final:
104 
105     sd_bus_error_free(&error);
106     m = sd_bus_message_unref(m);
107     reply = sd_bus_message_unref(reply);
108 
109     return r;
110 }
111 
112 int get_bus_for_path(const char *path, char **busname) {
113     return mapper_get_service(bus, path, busname);
114 }
115 
116 int legacy_dbus_openbmc_path(const char *type, const uint8_t num, dbus_interface_t *interface) {
117     char  *busname = NULL;
118     const char  *iface = "org.openbmc.managers.System";
119     const char  *objname = "/org/openbmc/managers/System";
120     char  *str1 = NULL, *str2, *str3;
121     sd_bus_error error = SD_BUS_ERROR_NULL;
122     sd_bus_message *reply = NULL;
123 
124 
125     int r;
126     r = get_bus_for_path(objname, &busname);
127     if (r < 0) {
128         fprintf(stderr, "Failed to get %s busname: %s\n",
129                 objname, strerror(-r));
130         goto final;
131     }
132 
133     r = sd_bus_call_method(bus,busname,objname,iface, "getObjectFromByteId",
134                            &error, &reply, "sy", type, num);
135     if (r < 0) {
136         fprintf(stderr, "Failed to create a method call: %s", strerror(-r));
137         goto final;
138     }
139 
140     r = sd_bus_message_read(reply, "(ss)", &str2, &str3);
141     if (r < 0) {
142         fprintf(stderr, "Failed to get a response: %s", strerror(-r));
143         goto final;
144     }
145 
146     r = get_bus_for_path(str2, &str1);
147     if (r < 0) {
148         fprintf(stderr, "Failed to get %s busname: %s\n",
149                 str2, strerror(-r));
150         goto final;
151     }
152 
153     strncpy(interface->bus, str1, MAX_DBUS_PATH);
154     strncpy(interface->path, str2, MAX_DBUS_PATH);
155     strncpy(interface->interface, str3, MAX_DBUS_PATH);
156 
157     interface->sensornumber = num;
158     // Make sure we know that the type hasn't been set, as newer codebase will
159     // set it automatically from the YAML at this step.
160     interface->sensortype = 0;
161 
162 final:
163 
164     sd_bus_error_free(&error);
165     reply = sd_bus_message_unref(reply);
166     free(busname);
167     free(str1);
168 
169     return r;
170 }
171 
172 // Use a lookup table to find the interface name of a specific sensor
173 // This will be used until an alternative is found.  this is the first
174 // step for mapping IPMI
175 int find_openbmc_path(uint8_t num, dbus_interface_t *interface) {
176     int rc;
177 
178     // When the sensor map does not contain the sensor requested,
179     // fall back to the legacy DBus lookup (deprecated)
180     const auto& sensor_it = sensors.find(num);
181     if (sensor_it == sensors.end())
182     {
183         return legacy_dbus_openbmc_path("SENSOR", num, interface);
184     }
185 
186     const auto& info = sensor_it->second;
187 
188     char* busname;
189     rc = get_bus_for_path(info.sensorPath.c_str(), &busname);
190     if (rc < 0) {
191         fprintf(stderr, "Failed to get %s busname: %s\n",
192                 info.sensorPath.c_str(),
193                 busname);
194         goto final;
195     }
196 
197     interface->sensortype = info.sensorType;
198     strcpy(interface->bus, busname);
199     strcpy(interface->path, info.sensorPath.c_str());
200     // Take the interface name from the beginning of the DbusInterfaceMap. This
201     // works for the Value interface but may not suffice for more complex
202     // sensors.
203     // tracked https://github.com/openbmc/phosphor-host-ipmid/issues/103
204     strcpy(interface->interface, info.sensorInterfaces.begin()->first.c_str());
205     interface->sensornumber = num;
206 
207 final:
208     free(busname);
209     return rc;
210 }
211 
212 
213 /////////////////////////////////////////////////////////////////////
214 //
215 // Routines used by ipmi commands wanting to interact on the dbus
216 //
217 /////////////////////////////////////////////////////////////////////
218 int set_sensor_dbus_state_s(uint8_t number, const char *method, const char *value) {
219 
220 
221     dbus_interface_t a;
222     int r;
223     sd_bus_error error = SD_BUS_ERROR_NULL;
224     sd_bus_message *m=NULL;
225 
226     fprintf(ipmidbus, "Attempting to set a dbus Variant Sensor 0x%02x via %s with a value of %s\n",
227         number, method, value);
228 
229     r = find_openbmc_path(number, &a);
230 
231     if (r < 0) {
232         fprintf(stderr, "Failed to find Sensor 0x%02x\n", number);
233         return 0;
234     }
235 
236     r = sd_bus_message_new_method_call(bus,&m,a.bus,a.path,a.interface,method);
237     if (r < 0) {
238         fprintf(stderr, "Failed to create a method call: %s", strerror(-r));
239         goto final;
240     }
241 
242     r = sd_bus_message_append(m, "v", "s", value);
243     if (r < 0) {
244         fprintf(stderr, "Failed to create a input parameter: %s", strerror(-r));
245         goto final;
246     }
247 
248 
249     r = sd_bus_call(bus, m, 0, &error, NULL);
250     if (r < 0) {
251         fprintf(stderr, "Failed to call the method: %s", strerror(-r));
252     }
253 
254 final:
255     sd_bus_error_free(&error);
256     m = sd_bus_message_unref(m);
257 
258     return 0;
259 }
260 int set_sensor_dbus_state_y(uint8_t number, const char *method, const uint8_t value) {
261 
262 
263     dbus_interface_t a;
264     int r;
265     sd_bus_error error = SD_BUS_ERROR_NULL;
266     sd_bus_message *m=NULL;
267 
268     fprintf(ipmidbus, "Attempting to set a dbus Variant Sensor 0x%02x via %s with a value of 0x%02x\n",
269         number, method, value);
270 
271     r = find_openbmc_path(number, &a);
272 
273     if (r < 0) {
274         fprintf(stderr, "Failed to find Sensor 0x%02x\n", number);
275         return 0;
276     }
277 
278     r = sd_bus_message_new_method_call(bus,&m,a.bus,a.path,a.interface,method);
279     if (r < 0) {
280         fprintf(stderr, "Failed to create a method call: %s", strerror(-r));
281         goto final;
282     }
283 
284     r = sd_bus_message_append(m, "v", "i", value);
285     if (r < 0) {
286         fprintf(stderr, "Failed to create a input parameter: %s", strerror(-r));
287         goto final;
288     }
289 
290 
291     r = sd_bus_call(bus, m, 0, &error, NULL);
292     if (r < 0) {
293         fprintf(stderr, "12 Failed to call the method: %s", strerror(-r));
294     }
295 
296 final:
297     sd_bus_error_free(&error);
298     m = sd_bus_message_unref(m);
299 
300     return 0;
301 }
302 
303 uint8_t dbus_to_sensor_type(char *p) {
304 
305     sensorTypemap_t *s = g_SensorTypeMap;
306     char r=0;
307 
308     while (s->number != 0xFF) {
309         if (!strcmp(s->dbusname,p)) {
310             r = s->number;
311              break;
312         }
313         s++;
314     }
315 
316 
317     if (s->number == 0xFF)
318         printf("Failed to find Sensor Type %s\n", p);
319 
320     return r;
321 }
322 
323 
324 uint8_t dbus_to_sensor_type_from_dbus(dbus_interface_t *a) {
325     char fru_type_name[64];
326     int r= 0;
327 
328     r = find_interface_property_fru_type(a, "fru_type", fru_type_name);
329     if (r<0) {
330         fprintf(stderr, "Failed to get a fru type: %s", strerror(-r));
331         return -1;
332     } else {
333         return dbus_to_sensor_type(fru_type_name);
334     }
335 }
336 
337 uint8_t get_type_from_interface(dbus_interface_t dbus_if) {
338 
339     char *p;
340     uint8_t type;
341 
342     // This is where sensors that do not exist in dbus but do
343     // exist in the host code stop.  This should indicate it
344     // is not a supported sensor
345     if (dbus_if.interface[0] == 0) { return 0;}
346 
347     // Fetch type from interface itself.
348     if (dbus_if.sensortype != 0)
349     {
350         type = dbus_if.sensortype;
351     }
352     // Legacy codebase does not populate type during initial handling:
353     else if (strstr(dbus_if.interface, "InventoryItem")) {
354         // InventoryItems are real frus.  So need to get the
355         // fru_type property
356         type = dbus_to_sensor_type_from_dbus(&dbus_if);
357     } else {
358         // Non InventoryItems
359         p = strrchr (dbus_if.path, '/');
360         type = dbus_to_sensor_type(p+1);
361     }
362 
363     return type;
364  }
365 
366 // Replaces find_sensor
367 uint8_t find_type_for_sensor_number(uint8_t num) {
368     int r;
369     dbus_interface_t dbus_if;
370     r = find_openbmc_path(num, &dbus_if);
371     if (r < 0) {
372         fprintf(stderr, "Could not find sensor %d\n", num);
373         return r;
374     }
375     return get_type_from_interface(dbus_if);
376 }
377 
378 
379 
380 
381 
382 ipmi_ret_t ipmi_sen_get_sensor_type(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
383                              ipmi_request_t request, ipmi_response_t response,
384                              ipmi_data_len_t data_len, ipmi_context_t context)
385 {
386     sensor_data_t *reqptr = (sensor_data_t*)request;
387     ipmi_ret_t rc = IPMI_CC_OK;
388 
389     printf("IPMI GET_SENSOR_TYPE [0x%02X]\n",reqptr->sennum);
390 
391     // TODO Not sure what the System-event-sensor is suppose to return
392     // need to ask Hostboot team
393     unsigned char buf[] = {0x00,0x6F};
394 
395     buf[0] = find_type_for_sensor_number(reqptr->sennum);
396 
397     // HACK UNTIL Dbus gets updated or we find a better way
398     if (buf[0] == 0) {
399         rc = IPMI_CC_SENSOR_INVALID;
400     }
401 
402 
403     *data_len = sizeof(buf);
404     memcpy(response, &buf, *data_len);
405 
406     return rc;
407 }
408 
409 ipmi_ret_t setSensorReading(void *request)
410 {
411     auto cmdData = static_cast<SetSensorReadingReq *>(request);
412 
413     auto assertionStates =
414             (static_cast<uint16_t>(cmdData->assertOffset8_14)) << 8 |
415             cmdData->assertOffset0_7;
416 
417     auto deassertionStates =
418             (static_cast<uint16_t>(cmdData->deassertOffset8_14)) << 8 |
419             cmdData->deassertOffset0_7;
420 
421     std::bitset<16> assertionSet(assertionStates);
422     std::bitset<16> deassertionSet(deassertionStates);
423 
424     // Check if the Sensor Number is present
425     auto iter = sensors.find(cmdData->number);
426     if (iter == sensors.end())
427     {
428         return IPMI_CC_SENSOR_INVALID;
429     }
430 
431     auto& interfaceList = iter->second.sensorInterfaces;
432     if (interfaceList.empty())
433     {
434         log<level::ERR>("Interface List empty for the sensor",
435                 entry("Sensor Number = %d", cmdData->number));
436         return IPMI_CC_UNSPECIFIED_ERROR;
437     }
438 
439     ipmi::sensor::ObjectMap objects;
440     ipmi::sensor::InterfaceMap interfaces;
441     for (const auto& interface : interfaceList)
442     {
443         for (const auto& property : interface.second)
444         {
445             ipmi::sensor::PropertyMap props;
446             bool valid = false;
447             for (const auto& value : property.second)
448             {
449                 if (assertionSet.test(value.first))
450                 {
451                     props.emplace(property.first, value.second.assert);
452                     valid = true;
453                 }
454                 else if (deassertionSet.test(value.first))
455                 {
456                     props.emplace(property.first, value.second.deassert);
457                     valid = true;
458                 }
459             }
460             if (valid)
461             {
462                 interfaces.emplace(interface.first, std::move(props));
463             }
464         }
465     }
466     objects.emplace(iter->second.sensorPath, std::move(interfaces));
467 
468     sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()};
469     using namespace std::string_literals;
470     static const auto intf = "xyz.openbmc_project.Inventory.Manager"s;
471     static const auto path = "/xyz/openbmc_project/inventory"s;
472     std::string service;
473 
474     try
475     {
476         service = ipmi::getService(bus, intf, path);
477 
478         // Update the inventory manager
479         auto pimMsg = bus.new_method_call(service.c_str(),
480                                           path.c_str(),
481                                           intf.c_str(),
482                                           "Notify");
483         pimMsg.append(std::move(objects));
484         auto inventoryMgrResponseMsg = bus.call(pimMsg);
485         if (inventoryMgrResponseMsg.is_method_error())
486         {
487             log<level::ERR>("Error in notify call");
488             return IPMI_CC_UNSPECIFIED_ERROR;
489         }
490     }
491     catch (const std::runtime_error& e)
492     {
493         log<level::ERR>(e.what());
494         return IPMI_CC_UNSPECIFIED_ERROR;
495     }
496 
497     return IPMI_CC_OK;
498 }
499 
500 ipmi_ret_t ipmi_sen_set_sensor(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
501                              ipmi_request_t request, ipmi_response_t response,
502                              ipmi_data_len_t data_len, ipmi_context_t context)
503 {
504     sensor_data_t *reqptr = (sensor_data_t*)request;
505 
506     printf("IPMI SET_SENSOR [0x%02x]\n",reqptr->sennum);
507 
508     /*
509      * This would support the Set Sensor Reading command for the presence
510      * and functional state of Processor, Core & DIMM. For the remaining
511      * sensors the existing support is invoked.
512      */
513     auto ipmiRC = setSensorReading(request);
514 
515     if(ipmiRC == IPMI_CC_SENSOR_INVALID)
516     {
517         updateSensorRecordFromSSRAESC(reqptr);
518         ipmiRC = IPMI_CC_OK;
519     }
520 
521     *data_len=0;
522     return ipmiRC;
523 }
524 
525 
526 ipmi_ret_t ipmi_sen_get_sensor_reading(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
527                              ipmi_request_t request, ipmi_response_t response,
528                              ipmi_data_len_t data_len, ipmi_context_t context)
529 {
530     sensor_data_t *reqptr = (sensor_data_t*)request;
531     ipmi_ret_t rc = IPMI_CC_SENSOR_INVALID;
532     uint8_t type;
533     sensorreadingresp_t *resp = (sensorreadingresp_t*) response;
534     int r;
535     dbus_interface_t a;
536     sd_bus *bus = ipmid_get_sd_bus_connection();
537     sd_bus_message *reply = NULL;
538     int reading = 0;
539 
540 
541     printf("IPMI GET_SENSOR_READING [0x%02x]\n",reqptr->sennum);
542 
543     r = find_openbmc_path(reqptr->sennum, &a);
544 
545     if (r < 0) {
546         fprintf(stderr, "Failed to find Sensor 0x%02x\n", reqptr->sennum);
547         return IPMI_CC_SENSOR_INVALID;
548     }
549 
550     type = get_type_from_interface(a);
551     if(type == 0) {
552         fprintf(stderr, "Failed to find Sensor 0x%02x\n", reqptr->sennum);
553         return IPMI_CC_SENSOR_INVALID;
554     }
555 
556     fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n", a.bus, a.path, a.interface);
557 
558     *data_len=0;
559 
560     int64_t raw_value, scale;
561     ipmi::sensor::Info sensor;
562 
563     switch(type) {
564         case 0xC3:
565         case 0xC2:
566             r = sd_bus_get_property(bus,a.bus, a.path, a.interface, "value", NULL, &reply, "i");
567             if (r < 0) {
568                 fprintf(stderr, "Failed to call sd_bus_get_property:%d,  %s\n", r, strerror(-r));
569                 fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n",
570                         a.bus, a.path, a.interface);
571                 break;
572             }
573 
574             r = sd_bus_message_read(reply, "i", &reading);
575             if (r < 0) {
576                 fprintf(stderr, "Failed to read sensor: %s\n", strerror(-r));
577                 break;
578             }
579 
580             printf("Contents of a 0x%02x is 0x%02x\n", type, reading);
581 
582             rc = IPMI_CC_OK;
583             *data_len=sizeof(sensorreadingresp_t);
584 
585             resp->value         = (uint8_t)reading;
586             resp->operation     = 0;
587             resp->indication[0] = 0;
588             resp->indication[1] = 0;
589             break;
590 
591         case IPMI_SENSOR_TEMP:
592         case IPMI_SENSOR_VOLTAGE:
593         case IPMI_SENSOR_CURRENT:
594         case IPMI_SENSOR_FAN:
595             // Get reading for /xyz/openbmc_project/Sensor/Value.interface
596 
597             // Get value
598             r = sd_bus_get_property_trivial(bus,
599                                             a.bus,
600                                             a.path,
601                                             a.interface,
602                                             "Value",
603                                             NULL,
604                                             'x',
605                                             &raw_value);
606             if (r < 0) {
607                 fprintf(stderr,
608                         "Failed to call sd_bus_get_property:%d,  %s, 'value'\n",
609                         r,
610                         strerror(-r));
611                 fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n",
612                         a.bus, a.path, a.interface);
613                 break;
614             }
615 
616             // Get scale
617             r = sd_bus_get_property_trivial(bus,
618                                             a.bus,
619                                             a.path,
620                                             a.interface,
621                                             "Scale",
622                                             NULL,
623                                             'x',
624                                             &scale);
625             if (r < 0) {
626                 fprintf(stderr,
627                         "Failed to call sd_bus_get_property:%d,  %s (scale)\n",
628                         r,
629                         strerror(-r));
630                 fprintf(stderr, "Bus: %s, Path: %s, Interface: %s\n",
631                         a.bus, a.path, a.interface);
632                 break;
633             }
634 
635             resp->value = raw_value * pow(10,scale);
636             resp->operation = 0;
637             resp->indication[0] = 0;
638             resp->indication[1] = 0;
639             rc = IPMI_CC_OK;
640             *data_len=sizeof(sensorreadingresp_t);
641             break;
642 
643         default:
644             *data_len=0;
645             rc = IPMI_CC_SENSOR_INVALID;
646             break;
647     }
648 
649 
650     reply = sd_bus_message_unref(reply);
651 
652     return rc;
653 }
654 
655 ipmi_ret_t ipmi_sen_wildcard(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
656                              ipmi_request_t request, ipmi_response_t response,
657                              ipmi_data_len_t data_len, ipmi_context_t context)
658 {
659     ipmi_ret_t rc = IPMI_CC_INVALID;
660 
661     printf("IPMI S/E Wildcard Netfn:[0x%X], Cmd:[0x%X]\n",netfn,cmd);
662     *data_len = 0;
663 
664     return rc;
665 }
666 
667 
668 void register_netfn_sen_functions()
669 {
670     // <Wildcard Command>
671     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_SENSOR, IPMI_CMD_WILDCARD);
672     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_WILDCARD, NULL, ipmi_sen_wildcard,
673                            PRIVILEGE_USER);
674 
675     // <Get Sensor Type>
676     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_TYPE);
677     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_TYPE, NULL, ipmi_sen_get_sensor_type,
678                            PRIVILEGE_USER);
679 
680     // <Set Sensor Reading and Event Status>
681     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_SENSOR, IPMI_CMD_SET_SENSOR);
682     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_SET_SENSOR, NULL, ipmi_sen_set_sensor,
683                            PRIVILEGE_OPERATOR);
684 
685     // <Get Sensor Reading>
686     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_READING);
687     ipmi_register_callback(NETFUN_SENSOR, IPMI_CMD_GET_SENSOR_READING, NULL,
688                            ipmi_sen_get_sensor_reading, PRIVILEGE_USER);
689 
690     return;
691 }
692