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