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