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