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