xref: /openbmc/fb-ipmi-oem/src/appcommands.cpp (revision 2405ae98b39a97e9d7753d8dc5c673a4a8885b85)
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 
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     uint8_t param = req[0];
317     uint8_t req_len = req.size();
318     std::optional<size_t> hostId = findHost(ctx->hostIdx);
319 
320     if (!hostId)
321     {
322         phosphor::logging::log<phosphor::logging::level::ERR>(
323             "Invalid Host Id received");
324         return ipmi::responseInvalidCommand();
325     }
326 
327     switch (param)
328     {
329         case SYS_INFO_PARAM_SET_IN_PROG:
330             sysInfoParams.set_in_prog = req[1];
331             break;
332         case SYS_INFO_PARAM_SYSFW_VER:
333         {
334             memcpy(sysInfoParams.sysfw_ver, &req[1], SIZE_SYSFW_VER);
335             std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId);
336             if (platSetSysFWVer(sysInfoParams.sysfw_ver, version_key))
337                 return ipmi::responseSystemInfoParamterNotSupportCommand();
338             break;
339         }
340         case SYS_INFO_PARAM_SYS_NAME:
341             memcpy(sysInfoParams.sys_name, &req[1], SIZE_SYS_NAME);
342             break;
343         case SYS_INFO_PARAM_PRI_OS_NAME:
344             memcpy(sysInfoParams.pri_os_name, &req[1], SIZE_OS_NAME);
345             break;
346         case SYS_INFO_PARAM_PRESENT_OS_NAME:
347             memcpy(sysInfoParams.present_os_name, &req[1], SIZE_OS_NAME);
348             break;
349         case SYS_INFO_PARAM_PRESENT_OS_VER:
350             memcpy(sysInfoParams.present_os_ver, &req[1], SIZE_OS_VER);
351             break;
352         case SYS_INFO_PARAM_BMC_URL:
353             memcpy(sysInfoParams.bmc_url, &req[1], SIZE_BMC_URL);
354             break;
355         case SYS_INFO_PARAM_OS_HV_URL:
356             memcpy(sysInfoParams.os_hv_url, &req[1], SIZE_OS_HV_URL);
357             break;
358         case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
359             memcpy(sysInfoParams.bios_current_boot_list, &req[1], req_len);
360             appData[KEY_BIOS_BOOT_LEN] = req_len;
361             flush_app_data();
362             break;
363         case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
364             if (SIZE_BIOS_FIXED_BOOT_DEVICE != req_len)
365                 break;
366             memcpy(sysInfoParams.bios_fixed_boot_device, &req[1],
367                    SIZE_BIOS_FIXED_BOOT_DEVICE);
368             break;
369         case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
370             if (SIZE_BIOS_RSTR_DFLT_SETTING != req_len)
371                 break;
372             memcpy(sysInfoParams.bios_rstr_dflt_setting, &req[1],
373                    SIZE_BIOS_RSTR_DFLT_SETTING);
374             break;
375         case SYS_INFO_PARAM_LAST_BOOT_TIME:
376             if (SIZE_LAST_BOOT_TIME != req_len)
377                 break;
378             memcpy(sysInfoParams.last_boot_time, &req[1], SIZE_LAST_BOOT_TIME);
379             break;
380         default:
381             return ipmi::responseSystemInfoParamterNotSupportCommand();
382             break;
383     }
384 
385     return ipmi::responseSuccess();
386 }
387 
388 //----------------------------------------------------------------------
389 // Get Sys Info Params (IPMI/Sec 22.14b) (CMD_APP_GET_SYS_INFO_PARAMS)
390 //----------------------------------------------------------------------
391 ipmi::RspType<std::vector<uint8_t>>
392     ipmiAppGetSysInfoParams(ipmi::Context::ptr ctx, uint8_t, uint8_t param,
393                             uint8_t, uint8_t)
394 {
395     int len;
396     std::vector<uint8_t> respData;
397     respData.push_back(1); // Parameter revision
398 
399     std::optional<size_t> hostId = findHost(ctx->hostIdx);
400 
401     if (!hostId)
402     {
403         phosphor::logging::log<phosphor::logging::level::ERR>(
404             "Invalid Host Id received");
405         return ipmi::responseInvalidCommand();
406     }
407 
408     switch (param)
409     {
410         case SYS_INFO_PARAM_SET_IN_PROG:
411             respData.push_back(sysInfoParams.set_in_prog);
412             break;
413         case SYS_INFO_PARAM_SYSFW_VER:
414         {
415             std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId);
416             if ((platGetSysFWVer(respData, version_key)) < 0)
417                 return ipmi::responseSystemInfoParamterNotSupportCommand();
418             break;
419         }
420         case SYS_INFO_PARAM_SYS_NAME:
421             respData.insert(respData.end(), std::begin(sysInfoParams.sys_name),
422                             std::end(sysInfoParams.sys_name));
423             break;
424         case SYS_INFO_PARAM_PRI_OS_NAME:
425             respData.insert(respData.end(),
426                             std::begin(sysInfoParams.pri_os_name),
427                             std::end(sysInfoParams.pri_os_name));
428             break;
429         case SYS_INFO_PARAM_PRESENT_OS_NAME:
430             respData.insert(respData.end(),
431                             std::begin(sysInfoParams.present_os_name),
432                             std::end(sysInfoParams.present_os_name));
433             break;
434         case SYS_INFO_PARAM_PRESENT_OS_VER:
435             respData.insert(respData.end(),
436                             std::begin(sysInfoParams.present_os_ver),
437                             std::end(sysInfoParams.present_os_ver));
438             break;
439         case SYS_INFO_PARAM_BMC_URL:
440             respData.insert(respData.end(), std::begin(sysInfoParams.bmc_url),
441                             std::end(sysInfoParams.bmc_url));
442             break;
443         case SYS_INFO_PARAM_OS_HV_URL:
444             respData.insert(respData.end(), std::begin(sysInfoParams.os_hv_url),
445                             std::end(sysInfoParams.os_hv_url));
446             break;
447         case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
448             len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>();
449             respData.insert(respData.end(),
450                             std::begin(sysInfoParams.bios_current_boot_list),
451                             std::begin(sysInfoParams.bios_current_boot_list) +
452                                 len);
453             break;
454         case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
455             respData.insert(respData.end(),
456                             std::begin(sysInfoParams.bios_fixed_boot_device),
457                             std::end(sysInfoParams.bios_fixed_boot_device));
458             break;
459         case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
460             respData.insert(respData.end(),
461                             std::begin(sysInfoParams.bios_rstr_dflt_setting),
462                             std::end(sysInfoParams.bios_rstr_dflt_setting));
463             break;
464         case SYS_INFO_PARAM_LAST_BOOT_TIME:
465             respData.insert(respData.end(),
466                             std::begin(sysInfoParams.last_boot_time),
467                             std::end(sysInfoParams.last_boot_time));
468             break;
469         default:
470             return ipmi::responseSystemInfoParamterNotSupportCommand();
471             break;
472     }
473 
474     return ipmi::responseSuccess(respData);
475 }
476 
477 void registerAPPFunctions()
478 {
479     /* Get App data stored in json file */
480     std::ifstream file(JSON_APP_DATA_FILE);
481     if (file)
482     {
483         file >> appData;
484         file.close();
485     }
486 
487     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SELFTEST_RESULTS, NULL,
488                          ipmiAppGetSTResults,
489                          PRIVILEGE_USER); // Get Self Test Results
490     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_MFR_TEST_ON, NULL,
491                          ipmiAppMfrTestOn,
492                          PRIVILEGE_USER); // Manufacturing Test On
493     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_GLOBAL_ENABLES, NULL,
494                          ipmiAppSetGlobalEnables,
495                          PRIVILEGE_USER); // Set Global Enables
496     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_GLOBAL_ENABLES, NULL,
497                          ipmiAppGetGlobalEnables,
498                          PRIVILEGE_USER); // Get Global Enables
499     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL,
500                          ipmiAppClearMsgFlags,
501                          PRIVILEGE_USER); // Clear Message flags
502 #if BIC_ENABLED
503     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
504                           ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
505                           ipmiAppGetSysGUID);
506 #else
507     ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_GUID, NULL,
508                          ipmiAppGetSysGUID,
509                          PRIVILEGE_USER); // Get System GUID
510 #endif
511     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
512                           ipmi::app::cmdSetSystemInfoParameters,
513                           ipmi::Privilege::User, ipmiAppSetSysInfoParams);
514 
515     ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
516                           ipmi::app::cmdGetSystemInfoParameters,
517                           ipmi::Privilege::User, ipmiAppGetSysInfoParams);
518     return;
519 }
520 
521 } // namespace ipmi
522