xref: /openbmc/fb-ipmi-oem/src/appcommands.cpp (revision 5f8e343516c0db488e977084ca7810a6c9fafae8)
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 (int 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 netfn, ipmi_cmd_t cmd,
116                                ipmi_request_t request, ipmi_response_t response,
117                                ipmi_data_len_t data_len, ipmi_context_t context)
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 netfn, ipmi_cmd_t cmd,
134                             ipmi_request_t request, ipmi_response_t response,
135                             ipmi_data_len_t data_len, ipmi_context_t context)
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 netfn, ipmi_cmd_t cmd,
160                                    ipmi_request_t request,
161                                    ipmi_response_t response,
162                                    ipmi_data_len_t data_len,
163                                    ipmi_context_t context)
164 {
165     uint8_t* req = reinterpret_cast<uint8_t*>(request);
166 
167     globEna = *req;
168     *data_len = 0;
169 
170     return IPMI_CC_OK;
171 }
172 
173 //----------------------------------------------------------------------
174 // Get Global Enables (CMD_APP_GET_GLOBAL_ENABLES)
175 //----------------------------------------------------------------------
176 ipmi_ret_t ipmiAppGetGlobalEnables(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
177                                    ipmi_request_t request,
178                                    ipmi_response_t response,
179                                    ipmi_data_len_t data_len,
180                                    ipmi_context_t context)
181 {
182     uint8_t* res = reinterpret_cast<uint8_t*>(response);
183 
184     *data_len = 1;
185     *res++ = globEna;
186 
187     return IPMI_CC_OK;
188 }
189 
190 //----------------------------------------------------------------------
191 // Clear Message flags (IPMI/Section 22.3) (CMD_APP_CLEAR_MESSAGE_FLAGS)
192 //----------------------------------------------------------------------
193 ipmi_ret_t ipmiAppClearMsgFlags(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
194                                 ipmi_request_t request,
195                                 ipmi_response_t response,
196                                 ipmi_data_len_t data_len,
197                                 ipmi_context_t context)
198 {
199     // Do Nothing and just return success
200     *data_len = 0;
201 
202     return IPMI_CC_OK;
203 }
204 
205 //----------------------------------------------------------------------
206 // Get System GUID (CMD_APP_GET_SYS_GUID)
207 //----------------------------------------------------------------------
208 #if BIC_ENABLED
209 ipmi::RspType<std::vector<uint8_t>>
210     ipmiAppGetSysGUID(ipmi::Context::ptr ctx, std::vector<uint8_t> reqData)
211 
212 {
213     std::vector<uint8_t> respData;
214 
215     uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2;
216 
217     if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData))
218         return ipmi::responseUnspecifiedError();
219 
220     return ipmi::responseSuccess(respData);
221 }
222 
223 #else
224 ipmi_ret_t ipmiAppGetSysGUID(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
225                              ipmi_request_t request, ipmi_response_t response,
226                              ipmi_data_len_t data_len, ipmi_context_t context)
227 {
228     uint8_t* res = reinterpret_cast<uint8_t*>(response);
229     if (getSystemGUID(res))
230     {
231         return IPMI_CC_UNSPECIFIED_ERROR;
232     }
233     *data_len = GUID_SIZE;
234     return IPMI_CC_OK;
235 }
236 
237 #endif
238 
239 //----------------------------------------------------------------------
240 // Platform specific functions for storing app data
241 //----------------------------------------------------------------------
242 
243 void flush_app_data()
244 {
245     std::ofstream file(JSON_APP_DATA_FILE);
246     file << appData;
247     file.close();
248     return;
249 }
250 
251 static int platSetSysFWVer(uint8_t* ver)
252 {
253     std::stringstream ss;
254     int i;
255 
256     /* TODO: implement byte 1: Set selector
257      * byte 2: encodeing, currently only supported
258      * ASCII which is value 0, UTF and unicode are
259      * not supported yet.
260      */
261     if (ver[1] & 0x0f)
262         return -1;
263 
264     for (i = 3; i < 3 + ver[2]; i++)
265     {
266         ss << (char)ver[i];
267     }
268 
269     appData[KEY_SYSFW_VER] = ss.str();
270     flush_app_data();
271 
272     return 0;
273 }
274 
275 static int platGetSysFWVer(uint8_t* ver)
276 {
277     std::string str = appData[KEY_SYSFW_VER].get<std::string>();
278     int len;
279 
280     *ver++ = 0; // byte 1: Set selector not supported
281     *ver++ = 0; // byte 2: Only ASCII supported
282 
283     len = str.length();
284     *ver++ = len;
285     memcpy(ver, str.data(), len);
286 
287     return (len + 3);
288 }
289 
290 //----------------------------------------------------------------------
291 // Set Sys Info Params (IPMI/Sec 22.14a) (CMD_APP_SET_SYS_INFO_PARAMS)
292 //----------------------------------------------------------------------
293 ipmi_ret_t ipmiAppSetSysInfoParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
294                                    ipmi_request_t request,
295                                    ipmi_response_t response,
296                                    ipmi_data_len_t data_len,
297                                    ipmi_context_t context)
298 {
299     uint8_t* req = reinterpret_cast<uint8_t*>(request);
300 
301     uint8_t param = req[0];
302     uint8_t req_len = *data_len;
303 
304     *data_len = 0;
305 
306     switch (param)
307     {
308         case SYS_INFO_PARAM_SET_IN_PROG:
309             sysInfoParams.set_in_prog = req[1];
310             break;
311         case SYS_INFO_PARAM_SYSFW_VER:
312             memcpy(sysInfoParams.sysfw_ver, &req[1], SIZE_SYSFW_VER);
313             if (platSetSysFWVer(sysInfoParams.sysfw_ver))
314                 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
315             break;
316         case SYS_INFO_PARAM_SYS_NAME:
317             memcpy(sysInfoParams.sys_name, &req[1], SIZE_SYS_NAME);
318             break;
319         case SYS_INFO_PARAM_PRI_OS_NAME:
320             memcpy(sysInfoParams.pri_os_name, &req[1], SIZE_OS_NAME);
321             break;
322         case SYS_INFO_PARAM_PRESENT_OS_NAME:
323             memcpy(sysInfoParams.present_os_name, &req[1], SIZE_OS_NAME);
324             break;
325         case SYS_INFO_PARAM_PRESENT_OS_VER:
326             memcpy(sysInfoParams.present_os_ver, &req[1], SIZE_OS_VER);
327             break;
328         case SYS_INFO_PARAM_BMC_URL:
329             memcpy(sysInfoParams.bmc_url, &req[1], SIZE_BMC_URL);
330             break;
331         case SYS_INFO_PARAM_OS_HV_URL:
332             memcpy(sysInfoParams.os_hv_url, &req[1], SIZE_OS_HV_URL);
333             break;
334         case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
335             memcpy(sysInfoParams.bios_current_boot_list, &req[1], req_len);
336             appData[KEY_BIOS_BOOT_LEN] = req_len;
337             flush_app_data();
338             break;
339         case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
340             if (SIZE_BIOS_FIXED_BOOT_DEVICE != req_len)
341                 break;
342             memcpy(sysInfoParams.bios_fixed_boot_device, &req[1],
343                    SIZE_BIOS_FIXED_BOOT_DEVICE);
344             break;
345         case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
346             if (SIZE_BIOS_RSTR_DFLT_SETTING != req_len)
347                 break;
348             memcpy(sysInfoParams.bios_rstr_dflt_setting, &req[1],
349                    SIZE_BIOS_RSTR_DFLT_SETTING);
350             break;
351         case SYS_INFO_PARAM_LAST_BOOT_TIME:
352             if (SIZE_LAST_BOOT_TIME != req_len)
353                 break;
354             memcpy(sysInfoParams.last_boot_time, &req[1], SIZE_LAST_BOOT_TIME);
355             break;
356         default:
357             return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
358             break;
359     }
360 
361     return IPMI_CC_OK;
362 }
363 
364 //----------------------------------------------------------------------
365 // Get Sys Info Params (IPMI/Sec 22.14b) (CMD_APP_GET_SYS_INFO_PARAMS)
366 //----------------------------------------------------------------------
367 ipmi_ret_t ipmiAppGetSysInfoParams(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
368                                    ipmi_request_t request,
369                                    ipmi_response_t response,
370                                    ipmi_data_len_t data_len,
371                                    ipmi_context_t context)
372 {
373     uint8_t* req = reinterpret_cast<uint8_t*>(request);
374     uint8_t* res = reinterpret_cast<uint8_t*>(response);
375 
376     uint8_t param = req[1];
377     uint8_t len;
378 
379     *res++ = 1; // Parameter revision
380     *data_len = 1;
381 
382     switch (param)
383     {
384         case SYS_INFO_PARAM_SET_IN_PROG:
385             *res++ = sysInfoParams.set_in_prog;
386             *data_len += 1;
387             break;
388         case SYS_INFO_PARAM_SYSFW_VER:
389             if ((len = platGetSysFWVer(res)) < 0)
390                 return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
391             *data_len += SIZE_SYSFW_VER;
392             break;
393         case SYS_INFO_PARAM_SYS_NAME:
394             memcpy(res, sysInfoParams.sys_name, SIZE_SYS_NAME);
395             *data_len += SIZE_SYS_NAME;
396             break;
397         case SYS_INFO_PARAM_PRI_OS_NAME:
398             memcpy(res, sysInfoParams.pri_os_name, SIZE_OS_NAME);
399             *data_len += SIZE_OS_NAME;
400             break;
401         case SYS_INFO_PARAM_PRESENT_OS_NAME:
402             memcpy(res, sysInfoParams.present_os_name, SIZE_OS_NAME);
403             *data_len += SIZE_OS_NAME;
404             break;
405         case SYS_INFO_PARAM_PRESENT_OS_VER:
406             memcpy(res, sysInfoParams.present_os_ver, SIZE_OS_VER);
407             *data_len += SIZE_OS_VER;
408             break;
409         case SYS_INFO_PARAM_BMC_URL:
410             memcpy(res, sysInfoParams.bmc_url, SIZE_BMC_URL);
411             *data_len += SIZE_BMC_URL;
412             break;
413         case SYS_INFO_PARAM_OS_HV_URL:
414             memcpy(res, sysInfoParams.os_hv_url, SIZE_OS_HV_URL);
415             *data_len += SIZE_OS_HV_URL;
416             break;
417         case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
418             len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>();
419             memcpy(res, sysInfoParams.bios_current_boot_list, len);
420             *data_len += len;
421             break;
422         case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
423             memcpy(res, sysInfoParams.bios_fixed_boot_device,
424                    SIZE_BIOS_FIXED_BOOT_DEVICE);
425             *data_len += SIZE_BIOS_FIXED_BOOT_DEVICE;
426             break;
427         case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
428             memcpy(res, sysInfoParams.bios_rstr_dflt_setting,
429                    SIZE_BIOS_RSTR_DFLT_SETTING);
430             *data_len += SIZE_BIOS_RSTR_DFLT_SETTING;
431             break;
432         case SYS_INFO_PARAM_LAST_BOOT_TIME:
433             memcpy(res, sysInfoParams.last_boot_time, SIZE_LAST_BOOT_TIME);
434             *data_len += SIZE_LAST_BOOT_TIME;
435             break;
436         default:
437             return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
438             break;
439     }
440     return IPMI_CC_OK;
441 }
442 
443 void registerAPPFunctions()
444 {
445     /* Get App data stored in json file */
446     std::ifstream file(JSON_APP_DATA_FILE);
447     if (file)
448     {
449         file >> appData;
450         file.close();
451     }
452 
453     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SELFTEST_RESULTS, NULL,
454                          ipmiAppGetSTResults,
455                          PRIVILEGE_USER); // Get Self Test Results
456     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_MFR_TEST_ON, NULL,
457                          ipmiAppMfrTestOn,
458                          PRIVILEGE_USER); // Manufacturing Test On
459     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_GLOBAL_ENABLES, NULL,
460                          ipmiAppSetGlobalEnables,
461                          PRIVILEGE_USER); // Set Global Enables
462     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_GLOBAL_ENABLES, NULL,
463                          ipmiAppGetGlobalEnables,
464                          PRIVILEGE_USER); // Get Global Enables
465     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL,
466                          ipmiAppClearMsgFlags,
467                          PRIVILEGE_USER); // Clear Message flags
468 #if BIC_ENABLED
469     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
470                           ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
471                           ipmiAppGetSysGUID);
472 #else
473     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_GUID, NULL,
474                          ipmiAppGetSysGUID,
475                          PRIVILEGE_USER); // Get System GUID
476 #endif
477     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_SYS_INFO_PARAMS, NULL,
478                          ipmiAppSetSysInfoParams,
479                          PRIVILEGE_USER); // Set Sys Info Params
480     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_INFO_PARAMS, NULL,
481                          ipmiAppGetSysInfoParams,
482                          PRIVILEGE_USER); // Get Sys Info Params
483     return;
484 }
485 
486 } // namespace ipmi
487