1 #include "apphandler.h"
2 #include "app/channel.hpp"
3 #include "app/watchdog.hpp"
4 #include "host-ipmid/ipmid-api.h"
5 #include "ipmid.hpp"
6 #include "nlohmann/json.hpp"
7 #include "types.hpp"
8 #include "utils.hpp"
9 
10 #include <fstream>
11 #include <stdio.h>
12 #include <stdint.h>
13 #include <systemd/sd-bus.h>
14 #include <mapper.h>
15 #include <array>
16 #include <vector>
17 #include <string>
18 #include <cstddef>
19 #include <experimental/filesystem>
20 
21 #include <arpa/inet.h>
22 #include "transporthandler.hpp"
23 
24 #include <phosphor-logging/log.hpp>
25 #include <phosphor-logging/elog-errors.hpp>
26 #include "xyz/openbmc_project/Common/error.hpp"
27 
28 extern sd_bus *bus;
29 
30 constexpr auto app_obj = "/org/openbmc/NetworkManager/Interface";
31 constexpr auto app_ifc = "org.openbmc.NetworkManager";
32 constexpr auto app_nwinterface = "eth0";
33 
34 void register_netfn_app_functions() __attribute__((constructor));
35 
36 using namespace phosphor::logging;
37 using namespace sdbusplus::xyz::openbmc_project::Common::Error;
38 namespace fs = std::experimental::filesystem;
39 
40 // Offset in get device id command.
41 typedef struct
42 {
43    uint8_t id;
44    uint8_t revision;
45    uint8_t fw[2];
46    uint8_t ipmi_ver;
47    uint8_t addn_dev_support;
48    uint8_t manuf_id[3];
49    uint8_t prod_id[2];
50    uint8_t aux[4];
51 }__attribute__((packed)) ipmi_device_id_t;
52 
53 ipmi_ret_t ipmi_app_set_acpi_power_state(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
54                              ipmi_request_t request, ipmi_response_t response,
55                              ipmi_data_len_t data_len, ipmi_context_t context)
56 {
57     ipmi_ret_t rc = IPMI_CC_OK;
58     *data_len = 0;
59 
60     printf("IPMI SET ACPI STATE Ignoring for now\n");
61     return rc;
62 }
63 
64 typedef struct
65 {
66     char major;
67     char minor;
68     uint16_t d[2];
69 } rev_t;
70 
71 /* Currently supports the vx.x-x-[-x] and v1.x.x-x-[-x] format. It will     */
72 /* return -1 if not in those formats, this routine knows how to parse       */
73 /* version = v0.6-19-gf363f61-dirty                                         */
74 /*            ^ ^ ^^          ^                                             */
75 /*            | |  |----------|-- additional details                        */
76 /*            | |---------------- Minor                                     */
77 /*            |------------------ Major                                     */
78 /* and version = v1.99.10-113-g65edf7d-r3-0-g9e4f715                        */
79 /*                ^ ^  ^^ ^                                                 */
80 /*                | |  |--|---------- additional details                    */
81 /*                | |---------------- Minor                                 */
82 /*                |------------------ Major                                 */
83 /* Additional details : If the option group exists it will force Auxiliary  */
84 /* Firmware Revision Information 4th byte to 1 indicating the build was     */
85 /* derived with additional edits                                            */
86 int convert_version(const char * p, rev_t *rev)
87 {
88     std::string s(p);
89     std::string token;
90     uint16_t commits;
91 
92     auto location  = s.find_first_of('v');
93     if (location != std::string::npos)
94     {
95         s = s.substr(location+1);
96     }
97 
98     if (!s.empty())
99     {
100         location = s.find_first_of(".");
101         if (location != std::string::npos)
102         {
103             rev->major = static_cast<char>(std::stoi(s.substr(0, location), 0, 16));
104             token = s.substr(location+1);
105         }
106 
107         if (!token.empty())
108         {
109             location = token.find_first_of(".-");
110             if (location != std::string::npos)
111             {
112                 rev->minor = static_cast<char>(std::stoi(token.substr(0, location), 0, 16));
113                 token = token.substr(location+1);
114             }
115         }
116 
117         // Capture the number of commits on top of the minor tag.
118         // I'm using BE format like the ipmi spec asked for
119         location = token.find_first_of(".-");
120         if (!token.empty())
121         {
122             commits = std::stoi(token.substr(0, location), 0, 16);
123             rev->d[0] = (commits>>8) | (commits<<8);
124 
125             // commit number we skip
126             location = token.find_first_of(".-");
127             if (location != std::string::npos)
128             {
129                 token = token.substr(location+1);
130             }
131         }
132         else {
133             rev->d[0] = 0;
134         }
135 
136         if (location != std::string::npos)
137         {
138             token = token.substr(location+1);
139         }
140 
141         // Any value of the optional parameter forces it to 1
142         location = token.find_first_of(".-");
143         if (location != std::string::npos)
144         {
145             token = token.substr(location+1);
146         }
147         commits = (!token.empty()) ? 1 : 0;
148 
149         //We do this operation to get this displayed in least significant bytes
150         //of ipmitool device id command.
151         rev->d[1] = (commits>>8) | (commits<<8);
152     }
153 
154     return 0;
155 }
156 
157 ipmi_ret_t ipmi_app_get_device_id(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
158                              ipmi_request_t request, ipmi_response_t response,
159                              ipmi_data_len_t data_len, ipmi_context_t context)
160 {
161     ipmi_ret_t rc = IPMI_CC_OK;
162     const char  *objname = "/org/openbmc/inventory/system/chassis/motherboard/bmc";
163     const char  *iface   = "org.openbmc.InventoryItem";
164     char *ver = NULL;
165     char *busname = NULL;
166     int r;
167     rev_t rev = {0};
168     static ipmi_device_id_t dev_id{};
169     static bool dev_id_initialized = false;
170     const char* filename = "/usr/share/ipmi-providers/dev_id.json";
171 
172     // Data length
173     *data_len = sizeof(dev_id);
174 
175     if (!dev_id_initialized)
176     {
177         // Firmware revision is already implemented,
178         // so get it from appropriate position.
179         r = mapper_get_service(bus, objname, &busname);
180         if (r < 0) {
181             fprintf(stderr, "Failed to get %s bus name: %s\n",
182                     objname, strerror(-r));
183             goto finish;
184         }
185         r = sd_bus_get_property_string(bus,busname,objname,iface,"version",
186                 NULL, &ver);
187         if ( r < 0 ) {
188             fprintf(stderr, "Failed to obtain version property: %s\n",
189                     strerror(-r));
190         } else {
191             r = convert_version(ver, &rev);
192             if( r >= 0 ) {
193                 // bit7 identifies if the device is available
194                 // 0=normal operation
195                 // 1=device firmware, SDR update,
196                 // or self-initialization in progress.
197                 // our SDR is normal working condition, so mask:
198                 dev_id.fw[0] = 0x7F & rev.major;
199 
200                 rev.minor = (rev.minor > 99 ? 99 : rev.minor);
201                 dev_id.fw[1] = rev.minor % 10 + (rev.minor / 10) * 16;
202                 memcpy(&dev_id.aux, rev.d, 4);
203             }
204         }
205 
206         // IPMI Spec version 2.0
207         dev_id.ipmi_ver = 2;
208 
209         std::ifstream dev_id_file(filename);
210         if (dev_id_file.is_open())
211         {
212             auto data = nlohmann::json::parse(dev_id_file, nullptr, false);
213             if (!data.is_discarded())
214             {
215                 dev_id.id = data.value("id", 0);
216                 dev_id.revision = data.value("revision", 0);
217                 dev_id.addn_dev_support = data.value("addn_dev_support", 0);
218                 dev_id.manuf_id[2] = data.value("manuf_id", 0) >> 16;
219                 dev_id.manuf_id[1] = data.value("manuf_id", 0) >> 8;
220                 dev_id.manuf_id[0] = data.value("manuf_id", 0);
221                 dev_id.prod_id[1] = data.value("prod_id", 0) >> 8;
222                 dev_id.prod_id[0] = data.value("prod_id", 0);
223                 dev_id.aux[3] = data.value("aux", 0) >> 24;
224                 dev_id.aux[2] = data.value("aux", 0) >> 16;
225                 dev_id.aux[1] = data.value("aux", 0) >> 8;
226                 dev_id.aux[0] = data.value("aux", 0);
227 
228                 //Don't read the file every time if successful
229                 dev_id_initialized = true;
230             }
231             else
232             {
233                 log<level::ERR>("Device ID JSON parser failure");
234                 rc = IPMI_CC_UNSPECIFIED_ERROR;
235             }
236         }
237         else
238         {
239             log<level::ERR>("Device ID file not found");
240             rc = IPMI_CC_UNSPECIFIED_ERROR;
241         }
242     }
243 
244     // Pack the actual response
245     memcpy(response, &dev_id, *data_len);
246 finish:
247     free(busname);
248     free(ver);
249     return rc;
250 }
251 
252 ipmi_ret_t ipmi_app_get_self_test_results(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
253                              ipmi_request_t request, ipmi_response_t response,
254                              ipmi_data_len_t data_len, ipmi_context_t context)
255 {
256     ipmi_ret_t rc = IPMI_CC_OK;
257 
258     // Byte 2:
259     //  55h - No error.
260     //  56h - Self Test function not implemented in this controller.
261     //  57h - Corrupted or inaccesssible data or devices.
262     //  58h - Fatal hardware error.
263     //  FFh - reserved.
264     //  all other: Device-specific 'internal failure'.
265     //  Byte 3:
266     //      For byte 2 = 55h, 56h, FFh:     00h
267     //      For byte 2 = 58h, all other:    Device-specific
268     //      For byte 2 = 57h:   self-test error bitfield.
269     //      Note: returning 57h does not imply that all test were run.
270     //      [7] 1b = Cannot access SEL device.
271     //      [6] 1b = Cannot access SDR Repository.
272     //      [5] 1b = Cannot access BMC FRU device.
273     //      [4] 1b = IPMB signal lines do not respond.
274     //      [3] 1b = SDR Repository empty.
275     //      [2] 1b = Internal Use Area of BMC FRU corrupted.
276     //      [1] 1b = controller update 'boot block' firmware corrupted.
277     //      [0] 1b = controller operational firmware corrupted.
278 
279     char selftestresults[2] = {0};
280 
281     *data_len = 2;
282 
283     selftestresults[0] = 0x56;
284     selftestresults[1] = 0;
285 
286     memcpy(response, selftestresults, *data_len);
287 
288     return rc;
289 }
290 
291 ipmi_ret_t ipmi_app_get_device_guid(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
292                              ipmi_request_t request, ipmi_response_t response,
293                              ipmi_data_len_t data_len, ipmi_context_t context)
294 {
295     const char  *objname = "/org/openbmc/control/chassis0";
296     const char  *iface = "org.freedesktop.DBus.Properties";
297     const char  *chassis_iface = "org.openbmc.control.Chassis";
298     sd_bus_message *reply = NULL;
299     sd_bus_error error = SD_BUS_ERROR_NULL;
300     int r = 0;
301     char *uuid = NULL;
302     char *busname = NULL;
303 
304     // UUID is in RFC4122 format. Ex: 61a39523-78f2-11e5-9862-e6402cfc3223
305     // Per IPMI Spec 2.0 need to convert to 16 hex bytes and reverse the byte order
306     // Ex: 0x2332fc2c40e66298e511f2782395a361
307 
308     const int resp_size = 16; // Response is 16 hex bytes per IPMI Spec
309     uint8_t resp_uuid[resp_size]; // Array to hold the formatted response
310     int resp_loc = resp_size-1; // Point resp end of array to save in reverse order
311     int i = 0;
312     char *tokptr = NULL;
313     char *id_octet = NULL;
314 
315     // Status code.
316     ipmi_ret_t rc = IPMI_CC_OK;
317     *data_len = 0;
318 
319     printf("IPMI GET DEVICE GUID\n");
320 
321     // Call Get properties method with the interface and property name
322     r = mapper_get_service(bus, objname, &busname);
323     if (r < 0) {
324         fprintf(stderr, "Failed to get %s bus name: %s\n",
325                 objname, strerror(-r));
326         goto finish;
327     }
328     r = sd_bus_call_method(bus,busname,objname,iface,
329                            "Get",&error, &reply, "ss",
330                            chassis_iface, "uuid");
331     if (r < 0)
332     {
333         fprintf(stderr, "Failed to call Get Method: %s\n", strerror(-r));
334         rc = IPMI_CC_UNSPECIFIED_ERROR;
335         goto finish;
336     }
337 
338     r = sd_bus_message_read(reply, "v", "s", &uuid);
339     if (r < 0 || uuid == NULL)
340     {
341         fprintf(stderr, "Failed to get a response: %s", strerror(-r));
342         rc = IPMI_CC_RESPONSE_ERROR;
343         goto finish;
344     }
345 
346     // Traverse the UUID
347     id_octet = strtok_r(uuid, "-", &tokptr); // Get the UUID octects separated by dash
348 
349     if (id_octet == NULL)
350     {
351         // Error
352         fprintf(stderr, "Unexpected UUID format: %s", uuid);
353         rc = IPMI_CC_RESPONSE_ERROR;
354         goto finish;
355     }
356 
357     while (id_octet != NULL)
358     {
359         // Calculate the octet string size since it varies
360         // Divide it by 2 for the array size since 1 byte is built from 2 chars
361         int tmp_size = strlen(id_octet)/2;
362 
363         for(i = 0; i < tmp_size; i++)
364         {
365             char tmp_array[3] = {0}; // Holder of the 2 chars that will become a byte
366             strncpy(tmp_array, id_octet, 2); // 2 chars at a time
367 
368             int resp_byte = strtoul(tmp_array, NULL, 16); // Convert to hex byte
369             memcpy((void*)&resp_uuid[resp_loc], &resp_byte, 1); // Copy end to first
370             resp_loc--;
371             id_octet+=2; // Finished with the 2 chars, advance
372         }
373         id_octet=strtok_r(NULL, "-", &tokptr); // Get next octet
374     }
375 
376     // Data length
377     *data_len = resp_size;
378 
379     // Pack the actual response
380     memcpy(response, &resp_uuid, *data_len);
381 
382 finish:
383     sd_bus_error_free(&error);
384     reply = sd_bus_message_unref(reply);
385     free(busname);
386 
387     return rc;
388 }
389 
390 ipmi_ret_t ipmi_app_get_bt_capabilities(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
391                              ipmi_request_t request, ipmi_response_t response,
392                              ipmi_data_len_t data_len, ipmi_context_t context)
393 {
394     printf("Handling Netfn:[0x%X], Cmd:[0x%X]\n",netfn,cmd);
395 
396     // Status code.
397     ipmi_ret_t rc = IPMI_CC_OK;
398 
399     // Per IPMI 2.0 spec, the input and output buffer size must be the max
400     // buffer size minus one byte to allocate space for the length byte.
401     uint8_t str[] = {0x01, MAX_IPMI_BUFFER-1, MAX_IPMI_BUFFER-1, 0x0A, 0x01};
402 
403     // Data length
404     *data_len = sizeof(str);
405 
406     // Pack the actual response
407     memcpy(response, &str, *data_len);
408 
409     return rc;
410 }
411 
412 ipmi_ret_t ipmi_app_wildcard_handler(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
413                               ipmi_request_t request, ipmi_response_t response,
414                               ipmi_data_len_t data_len, ipmi_context_t context)
415 {
416     printf("Handling WILDCARD Netfn:[0x%X], Cmd:[0x%X]\n",netfn, cmd);
417 
418     // Status code.
419     ipmi_ret_t rc = IPMI_CC_INVALID;
420 
421     *data_len = strlen("THIS IS WILDCARD");
422 
423     // Now pack actual response
424     memcpy(response, "THIS IS WILDCARD", *data_len);
425 
426     return rc;
427 }
428 
429 void register_netfn_app_functions()
430 {
431     // <Get BT Interface Capabilities>
432     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_CAP_BIT);
433     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CAP_BIT, NULL, ipmi_app_get_bt_capabilities,
434                            PRIVILEGE_USER);
435 
436     // <Wildcard Command>
437     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_WILDCARD);
438     ipmi_register_callback(NETFUN_APP, IPMI_CMD_WILDCARD, NULL, ipmi_app_wildcard_handler,
439                            PRIVILEGE_USER);
440 
441     // <Reset Watchdog Timer>
442     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_RESET_WD);
443     ipmi_register_callback(NETFUN_APP, IPMI_CMD_RESET_WD, NULL, ipmi_app_reset_watchdog,
444                            PRIVILEGE_OPERATOR);
445 
446     // <Set Watchdog Timer>
447     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_SET_WD);
448     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_WD, NULL, ipmi_app_set_watchdog,
449                            PRIVILEGE_OPERATOR);
450 
451     // <Get Device ID>
452     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_DEVICE_ID);
453     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_ID, NULL, ipmi_app_get_device_id,
454                            PRIVILEGE_USER);
455 
456     // <Get Self Test Results>
457     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS);
458     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_SELF_TEST_RESULTS, NULL,
459                     ipmi_app_get_self_test_results, PRIVILEGE_USER);
460 
461     // <Get Device GUID>
462     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID);
463     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_DEVICE_GUID, NULL, ipmi_app_get_device_guid,
464                            PRIVILEGE_USER);
465 
466     // <Set ACPI Power State>
467     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_SET_ACPI);
468     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_ACPI, NULL, ipmi_app_set_acpi_power_state,
469                            PRIVILEGE_ADMIN);
470 
471     // <Set Channel Access>
472     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP,
473                                             IPMI_CMD_SET_CHAN_ACCESS);
474     ipmi_register_callback(NETFUN_APP, IPMI_CMD_SET_CHAN_ACCESS, NULL,
475                                     ipmi_set_channel_access, PRIVILEGE_ADMIN);
476 
477     // <Get Channel Access>
478     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS);
479     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHANNEL_ACCESS, NULL,
480                            ipmi_get_channel_access, PRIVILEGE_USER);
481 
482     // <Get Channel Info Command>
483     printf("Registering NetFn:[0x%X], Cmd:[0x%X]\n",NETFUN_APP, IPMI_CMD_GET_CHAN_INFO);
484     ipmi_register_callback(NETFUN_APP, IPMI_CMD_GET_CHAN_INFO, NULL, ipmi_app_channel_info,
485                            PRIVILEGE_USER);
486 
487     return;
488 }
489 
490 
491