xref: /openbmc/fb-ipmi-oem/src/appcommands.cpp (revision 2515e0931ae981645cda726fd278e60b8f271f7c)
1 /*
2  * Copyright (c)  2018 Intel Corporation.
3  * Copyright (c)  2018-present Facebook.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 #include <ipmid/api.h>
19 
20 #include <sys/stat.h>
21 #include <fcntl.h>
22 #include <unistd.h>
23 #include <nlohmann/json.hpp>
24 #include <commandutils.hpp>
25 #include <iostream>
26 #include <iomanip>
27 #include <sstream>
28 #include <fstream>
29 #include <phosphor-logging/log.hpp>
30 #include <sdbusplus/message/types.hpp>
31 #include <appcommands.hpp>
32 
33 namespace ipmi
34 {
35 
36 static void registerAPPFunctions() __attribute__((constructor));
37 static constexpr size_t GUID_SIZE = 16;
38 // TODO Make offset and location runtime configurable to ensure we
39 // can make each define their own locations.
40 static constexpr off_t OFFSET_SYS_GUID = 0x17F0;
41 static constexpr const char *FRU_EEPROM = "/sys/bus/i2c/devices/6-0054/eeprom";
42 
43 // TODO: Need to store this info after identifying proper storage
44 static uint8_t globEna = 0x09;
45 static SysInfoParam sysInfoParams;
46 nlohmann::json appData __attribute__((init_priority(101)));
47 
48 void printGUID(uint8_t *guid, off_t offset)
49 {
50     std::cout << "Read GUID from offset : " << offset << " :\n";
51     for (int i = 0; i < GUID_SIZE; i++)
52     {
53         int data = guid[i];
54         std::cout << std::hex << data << " ";
55     }
56     std::cout << std::endl;
57 }
58 
59 int getGUID(off_t offset, uint8_t *guid)
60 {
61     int fd = -1;
62     ssize_t bytes_rd;
63     int ret = 0;
64 
65     errno = 0;
66 
67     // Check if file is present
68     if (access(FRU_EEPROM, F_OK) == -1)
69     {
70         std::cerr << "Unable to access: " << FRU_EEPROM << std::endl;
71         return errno;
72     }
73 
74     // Open the file
75     fd = open(FRU_EEPROM, O_RDONLY);
76     if (fd == -1)
77     {
78         std::cerr << "Unable to open: " << FRU_EEPROM << std::endl;
79         return errno;
80     }
81 
82     // seek to the offset
83     lseek(fd, offset, SEEK_SET);
84 
85     // Read bytes from location
86     bytes_rd = read(fd, guid, GUID_SIZE);
87     if (bytes_rd != GUID_SIZE)
88     {
89         phosphor::logging::log<phosphor::logging::level::ERR>(
90             "GUID read data from EEPROM failed");
91         ret = errno;
92     }
93     else
94     {
95         printGUID(guid, offset);
96     }
97     close(fd);
98     return ret;
99 }
100 
101 int getSystemGUID(uint8_t *guid)
102 {
103     return getGUID(OFFSET_SYS_GUID, guid);
104 }
105 
106 //----------------------------------------------------------------------
107 // Get Self Test Results (IPMI/Section 20.4) (CMD_APP_GET_SELFTEST_RESULTS)
108 //----------------------------------------------------------------------
109 ipmi_ret_t ipmiAppGetSTResults(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
110                                ipmi_request_t request, ipmi_response_t response,
111                                ipmi_data_len_t data_len, ipmi_context_t context)
112 {
113     uint8_t *res = reinterpret_cast<uint8_t *>(response);
114 
115     // TODO: Following data needs to be updated based on self-test results
116     *res++ = 0x55; // Self-Test result
117     *res++ = 0x00; // Extra error info in case of failure
118 
119     *data_len = 2;
120 
121     return IPMI_CC_OK;
122 }
123 
124 //----------------------------------------------------------------------
125 // Manufacturing Test On (IPMI/Section 20.5) (CMD_APP_MFR_TEST_ON)
126 //----------------------------------------------------------------------
127 ipmi_ret_t ipmiAppMfrTestOn(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
128                             ipmi_request_t request, ipmi_response_t response,
129                             ipmi_data_len_t data_len, ipmi_context_t context)
130 {
131     uint8_t *req = reinterpret_cast<uint8_t *>(request);
132     std::string mfrTest = "sled-cycle";
133 
134     if (!memcmp(req, mfrTest.data(), mfrTest.length()) &&
135         (*data_len == mfrTest.length()))
136     {
137         /* sled-cycle the BMC */
138         system("/usr/sbin/power-util sled-cycle");
139     }
140     else
141     {
142         return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
143     }
144 
145     *data_len = 0;
146 
147     return IPMI_CC_OK;
148 }
149 
150 //----------------------------------------------------------------------
151 // Set Global Enables (CMD_APP_SET_GLOBAL_ENABLES)
152 //----------------------------------------------------------------------
153 ipmi_ret_t ipmiAppSetGlobalEnables(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
154                                    ipmi_request_t request,
155                                    ipmi_response_t response,
156                                    ipmi_data_len_t data_len,
157                                    ipmi_context_t context)
158 {
159     uint8_t *req = reinterpret_cast<uint8_t *>(request);
160 
161     globEna = *req;
162     *data_len = 0;
163 
164     return IPMI_CC_OK;
165 }
166 
167 //----------------------------------------------------------------------
168 // Get Global Enables (CMD_APP_GET_GLOBAL_ENABLES)
169 //----------------------------------------------------------------------
170 ipmi_ret_t ipmiAppGetGlobalEnables(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
171                                    ipmi_request_t request,
172                                    ipmi_response_t response,
173                                    ipmi_data_len_t data_len,
174                                    ipmi_context_t context)
175 {
176     uint8_t *res = reinterpret_cast<uint8_t *>(response);
177 
178     *data_len = 1;
179     *res++ = globEna;
180 
181     return IPMI_CC_OK;
182 }
183 
184 //----------------------------------------------------------------------
185 // Clear Message flags (IPMI/Section 22.3) (CMD_APP_CLEAR_MESSAGE_FLAGS)
186 //----------------------------------------------------------------------
187 ipmi_ret_t ipmiAppClearMsgFlags(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
188                                 ipmi_request_t request,
189                                 ipmi_response_t response,
190                                 ipmi_data_len_t data_len,
191                                 ipmi_context_t context)
192 {
193     // Do Nothing and just return success
194     *data_len = 0;
195 
196     return IPMI_CC_OK;
197 }
198 
199 //----------------------------------------------------------------------
200 // Get System GUID (CMD_APP_GET_SYS_GUID)
201 //----------------------------------------------------------------------
202 ipmi_ret_t ipmiAppGetSysGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
203                              ipmi_request_t request, ipmi_response_t response,
204                              ipmi_data_len_t data_len, ipmi_context_t context)
205 {
206     uint8_t *res = reinterpret_cast<uint8_t *>(response);
207     if (getSystemGUID(res))
208     {
209         return IPMI_CC_UNSPECIFIED_ERROR;
210     }
211     *data_len = GUID_SIZE;
212     return IPMI_CC_OK;
213 }
214 
215 //----------------------------------------------------------------------
216 // Platform specific functions for storing app data
217 //----------------------------------------------------------------------
218 
219 void flush_app_data()
220 {
221     std::ofstream file(JSON_APP_DATA_FILE);
222     file << appData;
223     file.close();
224     return;
225 }
226 
227 static int platSetSysFWVer(uint8_t *ver)
228 {
229     std::stringstream ss;
230     int i;
231 
232     /* TODO: implement byte 1: Set selector
233      * byte 2: encodeing, currently only supported
234      * ASCII which is value 0, UTF and unicode are
235      * not supported yet.
236      */
237     if (ver[1] & 0x0f)
238         return -1;
239 
240     for (i = 3; i < 3 + ver[2]; i++)
241     {
242         ss << (char)ver[i];
243     }
244 
245     appData[KEY_SYSFW_VER] = ss.str();
246     flush_app_data();
247 
248     return 0;
249 }
250 
251 static int platGetSysFWVer(uint8_t *ver)
252 {
253     std::string str = appData[KEY_SYSFW_VER].get<std::string>();
254     int len;
255 
256     *ver++ = 0; // byte 1: Set selector not supported
257     *ver++ = 0; // byte 2: Only ASCII supported
258 
259     len = str.length();
260     *ver++ = len;
261     memcpy(ver, str.data(), len);
262 
263     return (len + 3);
264 }
265 
266 //----------------------------------------------------------------------
267 // Set Sys Info Params (IPMI/Sec 22.14a) (CMD_APP_SET_SYS_INFO_PARAMS)
268 //----------------------------------------------------------------------
269 ipmi_ret_t ipmiAppSetSysInfoParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
270                                    ipmi_request_t request,
271                                    ipmi_response_t response,
272                                    ipmi_data_len_t data_len,
273                                    ipmi_context_t context)
274 {
275     uint8_t *req = reinterpret_cast<uint8_t *>(request);
276 
277     uint8_t param = req[0];
278     uint8_t req_len = *data_len;
279 
280     *data_len = 0;
281 
282     switch (param)
283     {
284         case SYS_INFO_PARAM_SET_IN_PROG:
285             sysInfoParams.set_in_prog = req[1];
286             break;
287         case SYS_INFO_PARAM_SYSFW_VER:
288             memcpy(sysInfoParams.sysfw_ver, &req[1], SIZE_SYSFW_VER);
289             if (platSetSysFWVer(sysInfoParams.sysfw_ver))
290                 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
291             break;
292         case SYS_INFO_PARAM_SYS_NAME:
293             memcpy(sysInfoParams.sys_name, &req[1], SIZE_SYS_NAME);
294             break;
295         case SYS_INFO_PARAM_PRI_OS_NAME:
296             memcpy(sysInfoParams.pri_os_name, &req[1], SIZE_OS_NAME);
297             break;
298         case SYS_INFO_PARAM_PRESENT_OS_NAME:
299             memcpy(sysInfoParams.present_os_name, &req[1], SIZE_OS_NAME);
300             break;
301         case SYS_INFO_PARAM_PRESENT_OS_VER:
302             memcpy(sysInfoParams.present_os_ver, &req[1], SIZE_OS_VER);
303             break;
304         case SYS_INFO_PARAM_BMC_URL:
305             memcpy(sysInfoParams.bmc_url, &req[1], SIZE_BMC_URL);
306             break;
307         case SYS_INFO_PARAM_OS_HV_URL:
308             memcpy(sysInfoParams.os_hv_url, &req[1], SIZE_OS_HV_URL);
309             break;
310         case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
311             memcpy(sysInfoParams.bios_current_boot_list, &req[1], req_len);
312             appData[KEY_BIOS_BOOT_LEN] = req_len;
313             flush_app_data();
314             break;
315         case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
316             if (SIZE_BIOS_FIXED_BOOT_DEVICE != req_len)
317                 break;
318             memcpy(sysInfoParams.bios_fixed_boot_device, &req[1],
319                    SIZE_BIOS_FIXED_BOOT_DEVICE);
320             break;
321         case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
322             if (SIZE_BIOS_RSTR_DFLT_SETTING != req_len)
323                 break;
324             memcpy(sysInfoParams.bios_rstr_dflt_setting, &req[1],
325                    SIZE_BIOS_RSTR_DFLT_SETTING);
326             break;
327         case SYS_INFO_PARAM_LAST_BOOT_TIME:
328             if (SIZE_LAST_BOOT_TIME != req_len)
329                 break;
330             memcpy(sysInfoParams.last_boot_time, &req[1], SIZE_LAST_BOOT_TIME);
331             break;
332         default:
333             return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
334             break;
335     }
336 
337     return IPMI_CC_OK;
338 }
339 
340 //----------------------------------------------------------------------
341 // Get Sys Info Params (IPMI/Sec 22.14b) (CMD_APP_GET_SYS_INFO_PARAMS)
342 //----------------------------------------------------------------------
343 ipmi_ret_t ipmiAppGetSysInfoParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
344                                    ipmi_request_t request,
345                                    ipmi_response_t response,
346                                    ipmi_data_len_t data_len,
347                                    ipmi_context_t context)
348 {
349     uint8_t *req = reinterpret_cast<uint8_t *>(request);
350     uint8_t *res = reinterpret_cast<uint8_t *>(response);
351 
352     uint8_t param = req[1];
353     uint8_t len;
354 
355     *res++ = 1; // Parameter revision
356     *data_len = 1;
357 
358     switch (param)
359     {
360         case SYS_INFO_PARAM_SET_IN_PROG:
361             *res++ = sysInfoParams.set_in_prog;
362             *data_len += 1;
363             break;
364         case SYS_INFO_PARAM_SYSFW_VER:
365             if ((len = platGetSysFWVer(res)) < 0)
366                 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
367             *data_len += SIZE_SYSFW_VER;
368             break;
369         case SYS_INFO_PARAM_SYS_NAME:
370             memcpy(res, sysInfoParams.sys_name, SIZE_SYS_NAME);
371             *data_len += SIZE_SYS_NAME;
372             break;
373         case SYS_INFO_PARAM_PRI_OS_NAME:
374             memcpy(res, sysInfoParams.pri_os_name, SIZE_OS_NAME);
375             *data_len += SIZE_OS_NAME;
376             break;
377         case SYS_INFO_PARAM_PRESENT_OS_NAME:
378             memcpy(res, sysInfoParams.present_os_name, SIZE_OS_NAME);
379             *data_len += SIZE_OS_NAME;
380             break;
381         case SYS_INFO_PARAM_PRESENT_OS_VER:
382             memcpy(res, sysInfoParams.present_os_ver, SIZE_OS_VER);
383             *data_len += SIZE_OS_VER;
384             break;
385         case SYS_INFO_PARAM_BMC_URL:
386             memcpy(res, sysInfoParams.bmc_url, SIZE_BMC_URL);
387             *data_len += SIZE_BMC_URL;
388             break;
389         case SYS_INFO_PARAM_OS_HV_URL:
390             memcpy(res, sysInfoParams.os_hv_url, SIZE_OS_HV_URL);
391             *data_len += SIZE_OS_HV_URL;
392             break;
393         case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
394             len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>();
395             memcpy(res, sysInfoParams.bios_current_boot_list, len);
396             *data_len += len;
397             break;
398         case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
399             memcpy(res, sysInfoParams.bios_fixed_boot_device,
400                    SIZE_BIOS_FIXED_BOOT_DEVICE);
401             *data_len += SIZE_BIOS_FIXED_BOOT_DEVICE;
402             break;
403         case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
404             memcpy(res, sysInfoParams.bios_rstr_dflt_setting,
405                    SIZE_BIOS_RSTR_DFLT_SETTING);
406             *data_len += SIZE_BIOS_RSTR_DFLT_SETTING;
407             break;
408         case SYS_INFO_PARAM_LAST_BOOT_TIME:
409             memcpy(res, sysInfoParams.last_boot_time, SIZE_LAST_BOOT_TIME);
410             *data_len += SIZE_LAST_BOOT_TIME;
411             break;
412         default:
413             return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
414             break;
415     }
416     return IPMI_CC_OK;
417 }
418 
419 void registerAPPFunctions()
420 {
421     /* Get App data stored in json file */
422     std::ifstream file(JSON_APP_DATA_FILE);
423     if (file)
424     {
425         file >> appData;
426         file.close();
427     }
428 
429     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SELFTEST_RESULTS, NULL,
430                          ipmiAppGetSTResults,
431                          PRIVILEGE_USER); // Get Self Test Results
432     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_MFR_TEST_ON, NULL,
433                          ipmiAppMfrTestOn,
434                          PRIVILEGE_USER); // Manufacturing Test On
435     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_GLOBAL_ENABLES, NULL,
436                          ipmiAppSetGlobalEnables,
437                          PRIVILEGE_USER); // Set Global Enables
438     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_GLOBAL_ENABLES, NULL,
439                          ipmiAppGetGlobalEnables,
440                          PRIVILEGE_USER); // Get Global Enables
441     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL,
442                          ipmiAppClearMsgFlags,
443                          PRIVILEGE_USER); // Clear Message flags
444     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_GUID, NULL,
445                          ipmiAppGetSysGUID,
446                          PRIVILEGE_USER); // Get System GUID
447     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_SYS_INFO_PARAMS, NULL,
448                          ipmiAppSetSysInfoParams,
449                          PRIVILEGE_USER); // Set Sys Info Params
450     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_INFO_PARAMS, NULL,
451                          ipmiAppGetSysInfoParams,
452                          PRIVILEGE_USER); // Get Sys Info Params
453     return;
454 }
455 
456 } // namespace ipmi
457