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