blob: ecbc2b41e883e9ef91f491d604df703d24a26646 [file] [log] [blame]
/*
* 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