xref: /openbmc/fb-ipmi-oem/src/appcommands.cpp (revision 1caf04862ed2a8c65f441f5762ec762bf912248e)
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  
responseSystemInfoParamterNotSupportCommand()54  static inline auto responseSystemInfoParamterNotSupportCommand()
55  {
56      return response(IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED);
57  }
58  
printGUID(uint8_t * guid,off_t offset)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  
getGUID(off_t offset,uint8_t * guid)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  
getSystemGUID(uint8_t * guid)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  //----------------------------------------------------------------------
ipmiAppGetSTResults(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)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  //----------------------------------------------------------------------
ipmiAppMfrTestOn(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)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  //----------------------------------------------------------------------
ipmiAppSetGlobalEnables(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t request,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)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  //----------------------------------------------------------------------
ipmiAppGetGlobalEnables(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)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  //----------------------------------------------------------------------
ipmiAppClearMsgFlags(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t,ipmi_data_len_t data_len,ipmi_context_t)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
ipmiAppGetSysGUID(ipmi::Context::ptr ctx,std::vector<uint8_t> reqData)226  ipmi::RspType<std::vector<uint8_t>> ipmiAppGetSysGUID(
227      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
ipmiAppGetSysGUID(ipmi_netfn_t,ipmi_cmd_t,ipmi_request_t,ipmi_response_t response,ipmi_data_len_t data_len,ipmi_context_t)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  
flush_app_data()260  void flush_app_data()
261  {
262      std::ofstream file(JSON_APP_DATA_FILE);
263      file << appData;
264      file.close();
265      return;
266  }
267  
platSetSysFWVer(uint8_t * ver,const std::string key)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  
platGetSysFWVer(std::vector<uint8_t> & respData,const std::string key)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  //----------------------------------------------------------------------
ipmiAppSetSysInfoParams(ipmi::Context::ptr ctx,std::vector<uint8_t> req)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  //----------------------------------------------------------------------
ipmiAppGetSysInfoParams(ipmi::Context::ptr ctx,uint8_t,uint8_t param,uint8_t,uint8_t)404  ipmi::RspType<std::vector<uint8_t>> ipmiAppGetSysInfoParams(
405      ipmi::Context::ptr ctx, uint8_t, uint8_t param, uint8_t, uint8_t)
406  {
407      int len;
408      std::vector<uint8_t> respData;
409      respData.push_back(1); // Parameter revision
410  
411      std::optional<size_t> hostId = findHost(ctx->hostIdx);
412  
413      if (!hostId)
414      {
415          phosphor::logging::log<phosphor::logging::level::ERR>(
416              "Invalid Host Id received");
417          return ipmi::responseInvalidCommand();
418      }
419  
420      switch (param)
421      {
422          case SYS_INFO_PARAM_SET_IN_PROG:
423              respData.push_back(sysInfoParams.set_in_prog);
424              break;
425          case SYS_INFO_PARAM_SYSFW_VER:
426          {
427              std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId);
428              if ((platGetSysFWVer(respData, version_key)) < 0)
429                  return ipmi::responseSystemInfoParamterNotSupportCommand();
430              break;
431          }
432          case SYS_INFO_PARAM_SYS_NAME:
433              respData.insert(respData.end(), std::begin(sysInfoParams.sys_name),
434                              std::end(sysInfoParams.sys_name));
435              break;
436          case SYS_INFO_PARAM_PRI_OS_NAME:
437              respData.insert(respData.end(),
438                              std::begin(sysInfoParams.pri_os_name),
439                              std::end(sysInfoParams.pri_os_name));
440              break;
441          case SYS_INFO_PARAM_PRESENT_OS_NAME:
442              respData.insert(respData.end(),
443                              std::begin(sysInfoParams.present_os_name),
444                              std::end(sysInfoParams.present_os_name));
445              break;
446          case SYS_INFO_PARAM_PRESENT_OS_VER:
447              respData.insert(respData.end(),
448                              std::begin(sysInfoParams.present_os_ver),
449                              std::end(sysInfoParams.present_os_ver));
450              break;
451          case SYS_INFO_PARAM_BMC_URL:
452              respData.insert(respData.end(), std::begin(sysInfoParams.bmc_url),
453                              std::end(sysInfoParams.bmc_url));
454              break;
455          case SYS_INFO_PARAM_OS_HV_URL:
456              respData.insert(respData.end(), std::begin(sysInfoParams.os_hv_url),
457                              std::end(sysInfoParams.os_hv_url));
458              break;
459          case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
460              len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>();
461              respData.insert(
462                  respData.end(),
463                  std::begin(sysInfoParams.bios_current_boot_list),
464                  std::begin(sysInfoParams.bios_current_boot_list) + len);
465              break;
466          case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
467              respData.insert(respData.end(),
468                              std::begin(sysInfoParams.bios_fixed_boot_device),
469                              std::end(sysInfoParams.bios_fixed_boot_device));
470              break;
471          case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
472              respData.insert(respData.end(),
473                              std::begin(sysInfoParams.bios_rstr_dflt_setting),
474                              std::end(sysInfoParams.bios_rstr_dflt_setting));
475              break;
476          case SYS_INFO_PARAM_LAST_BOOT_TIME:
477              respData.insert(respData.end(),
478                              std::begin(sysInfoParams.last_boot_time),
479                              std::end(sysInfoParams.last_boot_time));
480              break;
481          default:
482              return ipmi::responseSystemInfoParamterNotSupportCommand();
483              break;
484      }
485  
486      return ipmi::responseSuccess(respData);
487  }
488  
registerAPPFunctions()489  void registerAPPFunctions()
490  {
491      /* Get App data stored in json file */
492      std::ifstream file(JSON_APP_DATA_FILE);
493      if (file)
494      {
495          file >> appData;
496          file.close();
497      }
498  
499      ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SELFTEST_RESULTS, NULL,
500                           ipmiAppGetSTResults,
501                           PRIVILEGE_USER); // Get Self Test Results
502      ipmiPrintAndRegister(NETFUN_APP, CMD_APP_MFR_TEST_ON, NULL,
503                           ipmiAppMfrTestOn,
504                           PRIVILEGE_USER); // Manufacturing Test On
505      ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_GLOBAL_ENABLES, NULL,
506                           ipmiAppSetGlobalEnables,
507                           PRIVILEGE_USER); // Set Global Enables
508      ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_GLOBAL_ENABLES, NULL,
509                           ipmiAppGetGlobalEnables,
510                           PRIVILEGE_USER); // Get Global Enables
511      ipmiPrintAndRegister(NETFUN_APP, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL,
512                           ipmiAppClearMsgFlags,
513                           PRIVILEGE_USER); // Clear Message flags
514  #if BIC_ENABLED
515      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
516                            ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User,
517                            ipmiAppGetSysGUID);
518  #else
519      ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_GUID, NULL,
520                           ipmiAppGetSysGUID,
521                           PRIVILEGE_USER); // Get System GUID
522  #endif
523      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
524                            ipmi::app::cmdSetSystemInfoParameters,
525                            ipmi::Privilege::User, ipmiAppSetSysInfoParams);
526  
527      ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp,
528                            ipmi::app::cmdGetSystemInfoParameters,
529                            ipmi::Privilege::User, ipmiAppGetSysInfoParams);
530      return;
531  }
532  
533  } // namespace ipmi
534