/* * Copyright (c) 2018 Intel Corporation. * Copyright (c) 2018-present Facebook. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include <fcntl.h> #include <ipmid/api.h> #include <sys/stat.h> #include <unistd.h> #include <appcommands.hpp> #include <commandutils.hpp> #include <ipmid/api-types.hpp> #include <ipmid/api.hpp> #include <nlohmann/json.hpp> #include <phosphor-logging/log.hpp> #include <sdbusplus/message/types.hpp> #include <fstream> #include <iomanip> #include <iostream> #include <sstream> namespace ipmi { static void registerAPPFunctions() __attribute__((constructor)); static constexpr size_t GUID_SIZE = 16; // TODO Make offset and location runtime configurable to ensure we // can make each define their own locations. static constexpr off_t OFFSET_SYS_GUID = 0x17F0; static constexpr const char* FRU_EEPROM = "/sys/bus/i2c/devices/6-0054/eeprom"; // TODO: Need to store this info after identifying proper storage static uint8_t globEna = 0x09; static SysInfoParam sysInfoParams; nlohmann::json appData __attribute__((init_priority(101))); int sendBicCmd(uint8_t, uint8_t, uint8_t, std::vector<uint8_t>&, std::vector<uint8_t>&); static inline auto responseSystemInfoParamterNotSupportCommand() { return response(IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED); } void printGUID(uint8_t* guid, off_t offset) { std::cout << "Read GUID from offset : " << offset << " :\n"; for (size_t i = 0; i < GUID_SIZE; i++) { int data = guid[i]; std::cout << std::hex << data << " "; } std::cout << std::endl; } int getGUID(off_t offset, uint8_t* guid) { int fd = -1; ssize_t bytes_rd; int ret = 0; std::string eepromPath = FRU_EEPROM; // find the eeprom path of MB FRU auto device = getMbFruDevice(); if (device) { auto [bus, address] = *device; std::stringstream ss; ss << "/sys/bus/i2c/devices/" << static_cast<int>(bus) << "-" << std::setw(4) << std::setfill('0') << std::hex << static_cast<int>(address) << "/eeprom"; eepromPath = ss.str(); } errno = 0; // Check if file is present if (access(eepromPath.c_str(), F_OK) == -1) { std::cerr << "Unable to access: " << eepromPath << std::endl; return errno; } // Open the file fd = open(eepromPath.c_str(), O_RDONLY); if (fd == -1) { std::cerr << "Unable to open: " << eepromPath << std::endl; return errno; } // seek to the offset lseek(fd, offset, SEEK_SET); // Read bytes from location bytes_rd = read(fd, guid, GUID_SIZE); if (bytes_rd != GUID_SIZE) { phosphor::logging::log<phosphor::logging::level::ERR>( "GUID read data from EEPROM failed"); ret = errno; } else { printGUID(guid, offset); } close(fd); return ret; } int getSystemGUID(uint8_t* guid) { return getGUID(OFFSET_SYS_GUID, guid); } //---------------------------------------------------------------------- // Get Self Test Results (IPMI/Section 20.4) (CMD_APP_GET_SELFTEST_RESULTS) //---------------------------------------------------------------------- ipmi_ret_t ipmiAppGetSTResults(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t) { uint8_t* res = reinterpret_cast<uint8_t*>(response); // TODO: Following data needs to be updated based on self-test results *res++ = 0x55; // Self-Test result *res++ = 0x00; // Extra error info in case of failure *data_len = 2; return IPMI_CC_OK; } //---------------------------------------------------------------------- // Manufacturing Test On (IPMI/Section 20.5) (CMD_APP_MFR_TEST_ON) //---------------------------------------------------------------------- ipmi_ret_t ipmiAppMfrTestOn(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t, ipmi_data_len_t data_len, ipmi_context_t) { uint8_t* req = reinterpret_cast<uint8_t*>(request); std::string mfrTest = "sled-cycle"; ipmi_ret_t rc = IPMI_CC_OK; if (!memcmp(req, mfrTest.data(), mfrTest.length()) && (*data_len == mfrTest.length())) { /* sled-cycle the BMC */ auto ret = system("/usr/sbin/power-util sled-cycle"); if (ret) { rc = IPMI_CC_UNSPECIFIED_ERROR; } } else { rc = IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED; } *data_len = 0; return rc; } //---------------------------------------------------------------------- // Set Global Enables (CMD_APP_SET_GLOBAL_ENABLES) //---------------------------------------------------------------------- ipmi_ret_t ipmiAppSetGlobalEnables(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t request, ipmi_response_t, ipmi_data_len_t data_len, ipmi_context_t) { uint8_t* req = reinterpret_cast<uint8_t*>(request); globEna = *req; *data_len = 0; return IPMI_CC_OK; } //---------------------------------------------------------------------- // Get Global Enables (CMD_APP_GET_GLOBAL_ENABLES) //---------------------------------------------------------------------- ipmi_ret_t ipmiAppGetGlobalEnables(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t) { uint8_t* res = reinterpret_cast<uint8_t*>(response); *data_len = 1; *res++ = globEna; return IPMI_CC_OK; } //---------------------------------------------------------------------- // Clear Message flags (IPMI/Section 22.3) (CMD_APP_CLEAR_MESSAGE_FLAGS) //---------------------------------------------------------------------- ipmi_ret_t ipmiAppClearMsgFlags(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, ipmi_response_t, ipmi_data_len_t data_len, ipmi_context_t) { // Do Nothing and just return success *data_len = 0; return IPMI_CC_OK; } //---------------------------------------------------------------------- // Get System GUID (CMD_APP_GET_SYS_GUID) //---------------------------------------------------------------------- #if BIC_ENABLED ipmi::RspType<std::vector<uint8_t>> ipmiAppGetSysGUID(ipmi::Context::ptr ctx, std::vector<uint8_t> reqData) { std::vector<uint8_t> respData; uint8_t bicAddr = (uint8_t)ctx->hostIdx << 2; if (sendBicCmd(ctx->netFn, ctx->cmd, bicAddr, reqData, respData)) return ipmi::responseUnspecifiedError(); return ipmi::responseSuccess(respData); } #else ipmi_ret_t ipmiAppGetSysGUID(ipmi_netfn_t, ipmi_cmd_t, ipmi_request_t, ipmi_response_t response, ipmi_data_len_t data_len, ipmi_context_t) { uint8_t* res = reinterpret_cast<uint8_t*>(response); if (getSystemGUID(res)) { return IPMI_CC_UNSPECIFIED_ERROR; } *data_len = GUID_SIZE; return IPMI_CC_OK; } #endif //---------------------------------------------------------------------- // Platform specific functions for storing app data //---------------------------------------------------------------------- void flush_app_data() { std::ofstream file(JSON_APP_DATA_FILE); file << appData; file.close(); return; } static int platSetSysFWVer(uint8_t* ver, const std::string key) { std::stringstream ss; int i; /* TODO: implement byte 1: Set selector * byte 2: encodeing, currently only supported * ASCII which is value 0, UTF and unicode are * not supported yet. */ if (ver[1] & 0x0f) return -1; for (i = 3; i < 3 + ver[2]; i++) { ss << (char)ver[i]; } appData[key] = ss.str(); flush_app_data(); return 0; } static int platGetSysFWVer(std::vector<uint8_t>& respData, const std::string key) { int len = -1; if (!appData.contains(std::string(key))) { return -1; } std::string str = appData[key].get<std::string>(); respData.push_back(0); // byte 1: Set selector not supported respData.push_back(0); // byte 2: Only ASCII supported len = str.length(); respData.push_back(len); // byte 3: Size of version for (auto c : str) { respData.push_back(c); } // Remaining byte fill to 0 for (int i = 0; i < SIZE_SYSFW_VER - (len + 3); i++) { respData.push_back(0); } return (len + 3); } //---------------------------------------------------------------------- // Set Sys Info Params (IPMI/Sec 22.14a) (CMD_APP_SET_SYS_INFO_PARAMS) //---------------------------------------------------------------------- ipmi::RspType<uint8_t> ipmiAppSetSysInfoParams(ipmi::Context::ptr ctx, std::vector<uint8_t> req) { uint8_t param = req[0]; uint8_t req_len = req.size(); std::optional<size_t> hostId = findHost(ctx->hostIdx); if (!hostId) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Host Id received"); return ipmi::responseInvalidCommand(); } switch (param) { case SYS_INFO_PARAM_SET_IN_PROG: sysInfoParams.set_in_prog = req[1]; break; case SYS_INFO_PARAM_SYSFW_VER: { memcpy(sysInfoParams.sysfw_ver, &req[1], SIZE_SYSFW_VER); std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId); if (platSetSysFWVer(sysInfoParams.sysfw_ver, version_key)) return ipmi::responseSystemInfoParamterNotSupportCommand(); break; } case SYS_INFO_PARAM_SYS_NAME: memcpy(sysInfoParams.sys_name, &req[1], SIZE_SYS_NAME); break; case SYS_INFO_PARAM_PRI_OS_NAME: memcpy(sysInfoParams.pri_os_name, &req[1], SIZE_OS_NAME); break; case SYS_INFO_PARAM_PRESENT_OS_NAME: memcpy(sysInfoParams.present_os_name, &req[1], SIZE_OS_NAME); break; case SYS_INFO_PARAM_PRESENT_OS_VER: memcpy(sysInfoParams.present_os_ver, &req[1], SIZE_OS_VER); break; case SYS_INFO_PARAM_BMC_URL: memcpy(sysInfoParams.bmc_url, &req[1], SIZE_BMC_URL); break; case SYS_INFO_PARAM_OS_HV_URL: memcpy(sysInfoParams.os_hv_url, &req[1], SIZE_OS_HV_URL); break; case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST: memcpy(sysInfoParams.bios_current_boot_list, &req[1], req_len); appData[KEY_BIOS_BOOT_LEN] = req_len; flush_app_data(); break; case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE: if (SIZE_BIOS_FIXED_BOOT_DEVICE != req_len) break; memcpy(sysInfoParams.bios_fixed_boot_device, &req[1], SIZE_BIOS_FIXED_BOOT_DEVICE); break; case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING: if (SIZE_BIOS_RSTR_DFLT_SETTING != req_len) break; memcpy(sysInfoParams.bios_rstr_dflt_setting, &req[1], SIZE_BIOS_RSTR_DFLT_SETTING); break; case SYS_INFO_PARAM_LAST_BOOT_TIME: if (SIZE_LAST_BOOT_TIME != req_len) break; memcpy(sysInfoParams.last_boot_time, &req[1], SIZE_LAST_BOOT_TIME); break; default: return ipmi::responseSystemInfoParamterNotSupportCommand(); break; } return ipmi::responseSuccess(); } //---------------------------------------------------------------------- // Get Sys Info Params (IPMI/Sec 22.14b) (CMD_APP_GET_SYS_INFO_PARAMS) //---------------------------------------------------------------------- ipmi::RspType<std::vector<uint8_t>> ipmiAppGetSysInfoParams(ipmi::Context::ptr ctx, uint8_t, uint8_t param, uint8_t, uint8_t) { int len; std::vector<uint8_t> respData; respData.push_back(1); // Parameter revision std::optional<size_t> hostId = findHost(ctx->hostIdx); if (!hostId) { phosphor::logging::log<phosphor::logging::level::ERR>( "Invalid Host Id received"); return ipmi::responseInvalidCommand(); } switch (param) { case SYS_INFO_PARAM_SET_IN_PROG: respData.push_back(sysInfoParams.set_in_prog); break; case SYS_INFO_PARAM_SYSFW_VER: { std::string version_key = KEY_SYSFW_VER + std::to_string(*hostId); if ((platGetSysFWVer(respData, version_key)) < 0) return ipmi::responseSystemInfoParamterNotSupportCommand(); break; } case SYS_INFO_PARAM_SYS_NAME: respData.insert(respData.end(), std::begin(sysInfoParams.sys_name), std::end(sysInfoParams.sys_name)); break; case SYS_INFO_PARAM_PRI_OS_NAME: respData.insert(respData.end(), std::begin(sysInfoParams.pri_os_name), std::end(sysInfoParams.pri_os_name)); break; case SYS_INFO_PARAM_PRESENT_OS_NAME: respData.insert(respData.end(), std::begin(sysInfoParams.present_os_name), std::end(sysInfoParams.present_os_name)); break; case SYS_INFO_PARAM_PRESENT_OS_VER: respData.insert(respData.end(), std::begin(sysInfoParams.present_os_ver), std::end(sysInfoParams.present_os_ver)); break; case SYS_INFO_PARAM_BMC_URL: respData.insert(respData.end(), std::begin(sysInfoParams.bmc_url), std::end(sysInfoParams.bmc_url)); break; case SYS_INFO_PARAM_OS_HV_URL: respData.insert(respData.end(), std::begin(sysInfoParams.os_hv_url), std::end(sysInfoParams.os_hv_url)); break; case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST: len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>(); respData.insert(respData.end(), std::begin(sysInfoParams.bios_current_boot_list), std::begin(sysInfoParams.bios_current_boot_list) + len); break; case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE: respData.insert(respData.end(), std::begin(sysInfoParams.bios_fixed_boot_device), std::end(sysInfoParams.bios_fixed_boot_device)); break; case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING: respData.insert(respData.end(), std::begin(sysInfoParams.bios_rstr_dflt_setting), std::end(sysInfoParams.bios_rstr_dflt_setting)); break; case SYS_INFO_PARAM_LAST_BOOT_TIME: respData.insert(respData.end(), std::begin(sysInfoParams.last_boot_time), std::end(sysInfoParams.last_boot_time)); break; default: return ipmi::responseSystemInfoParamterNotSupportCommand(); break; } return ipmi::responseSuccess(respData); } void registerAPPFunctions() { /* Get App data stored in json file */ std::ifstream file(JSON_APP_DATA_FILE); if (file) { file >> appData; file.close(); } ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SELFTEST_RESULTS, NULL, ipmiAppGetSTResults, PRIVILEGE_USER); // Get Self Test Results ipmiPrintAndRegister(NETFUN_APP, CMD_APP_MFR_TEST_ON, NULL, ipmiAppMfrTestOn, PRIVILEGE_USER); // Manufacturing Test On ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_GLOBAL_ENABLES, NULL, ipmiAppSetGlobalEnables, PRIVILEGE_USER); // Set Global Enables ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_GLOBAL_ENABLES, NULL, ipmiAppGetGlobalEnables, PRIVILEGE_USER); // Get Global Enables ipmiPrintAndRegister(NETFUN_APP, CMD_APP_CLEAR_MESSAGE_FLAGS, NULL, ipmiAppClearMsgFlags, PRIVILEGE_USER); // Clear Message flags #if BIC_ENABLED ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetSystemGuid, ipmi::Privilege::User, ipmiAppGetSysGUID); #else ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_GUID, NULL, ipmiAppGetSysGUID, PRIVILEGE_USER); // Get System GUID #endif ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdSetSystemInfoParameters, ipmi::Privilege::User, ipmiAppSetSysInfoParams); ipmi::registerHandler(ipmi::prioOpenBmcBase, ipmi::netFnApp, ipmi::app::cmdGetSystemInfoParameters, ipmi::Privilege::User, ipmiAppGetSysInfoParams); return; } } // namespace ipmi