blob: a4aa3cce932acf9a6ac83c2722b195495c23ffe0 [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 <nlohmann/json.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/message/types.hpp>
#include <ipmid/api.hpp>
#include <ipmid/api-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>&);
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;
errno = 0;
// Check if file is present
if (access(FRU_EEPROM, F_OK) == -1)
{
std::cerr << "Unable to access: " << FRU_EEPROM << std::endl;
return errno;
}
// Open the file
fd = open(FRU_EEPROM, O_RDONLY);
if (fd == -1)
{
std::cerr << "Unable to open: " << FRU_EEPROM << 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)
{
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_SYSFW_VER] = ss.str();
flush_app_data();
return 0;
}
static int platGetSysFWVer(uint8_t* ver)
{
std::string str = appData[KEY_SYSFW_VER].get<std::string>();
int len;
*ver++ = 0; // byte 1: Set selector not supported
*ver++ = 0; // byte 2: Only ASCII supported
len = str.length();
*ver++ = len;
memcpy(ver, str.data(), len);
return (len + 3);
}
//----------------------------------------------------------------------
// Set Sys Info Params (IPMI/Sec 22.14a) (CMD_APP_SET_SYS_INFO_PARAMS)
//----------------------------------------------------------------------
ipmi_ret_t ipmiAppSetSysInfoParams(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);
uint8_t param = req[0];
uint8_t req_len = *data_len;
*data_len = 0;
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);
if (platSetSysFWVer(sysInfoParams.sysfw_ver))
return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
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_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
break;
}
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Get Sys Info Params (IPMI/Sec 22.14b) (CMD_APP_GET_SYS_INFO_PARAMS)
//----------------------------------------------------------------------
ipmi_ret_t ipmiAppGetSysInfoParams(ipmi_netfn_t, ipmi_cmd_t,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len, ipmi_context_t)
{
uint8_t* req = reinterpret_cast<uint8_t*>(request);
uint8_t* res = reinterpret_cast<uint8_t*>(response);
uint8_t param = req[1];
int len;
*res++ = 1; // Parameter revision
*data_len = 1;
switch (param)
{
case SYS_INFO_PARAM_SET_IN_PROG:
*res++ = sysInfoParams.set_in_prog;
*data_len += 1;
break;
case SYS_INFO_PARAM_SYSFW_VER:
if ((len = platGetSysFWVer(res)) < 0)
return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
*data_len += SIZE_SYSFW_VER;
break;
case SYS_INFO_PARAM_SYS_NAME:
memcpy(res, sysInfoParams.sys_name, SIZE_SYS_NAME);
*data_len += SIZE_SYS_NAME;
break;
case SYS_INFO_PARAM_PRI_OS_NAME:
memcpy(res, sysInfoParams.pri_os_name, SIZE_OS_NAME);
*data_len += SIZE_OS_NAME;
break;
case SYS_INFO_PARAM_PRESENT_OS_NAME:
memcpy(res, sysInfoParams.present_os_name, SIZE_OS_NAME);
*data_len += SIZE_OS_NAME;
break;
case SYS_INFO_PARAM_PRESENT_OS_VER:
memcpy(res, sysInfoParams.present_os_ver, SIZE_OS_VER);
*data_len += SIZE_OS_VER;
break;
case SYS_INFO_PARAM_BMC_URL:
memcpy(res, sysInfoParams.bmc_url, SIZE_BMC_URL);
*data_len += SIZE_BMC_URL;
break;
case SYS_INFO_PARAM_OS_HV_URL:
memcpy(res, sysInfoParams.os_hv_url, SIZE_OS_HV_URL);
*data_len += SIZE_OS_HV_URL;
break;
case SYS_INFO_PARAM_BIOS_CURRENT_BOOT_LIST:
len = appData[KEY_BIOS_BOOT_LEN].get<uint8_t>();
memcpy(res, sysInfoParams.bios_current_boot_list, len);
*data_len += len;
break;
case SYS_INFO_PARAM_BIOS_FIXED_BOOT_DEVICE:
memcpy(res, sysInfoParams.bios_fixed_boot_device,
SIZE_BIOS_FIXED_BOOT_DEVICE);
*data_len += SIZE_BIOS_FIXED_BOOT_DEVICE;
break;
case SYS_INFO_PARAM_BIOS_RSTR_DFLT_SETTING:
memcpy(res, sysInfoParams.bios_rstr_dflt_setting,
SIZE_BIOS_RSTR_DFLT_SETTING);
*data_len += SIZE_BIOS_RSTR_DFLT_SETTING;
break;
case SYS_INFO_PARAM_LAST_BOOT_TIME:
memcpy(res, sysInfoParams.last_boot_time, SIZE_LAST_BOOT_TIME);
*data_len += SIZE_LAST_BOOT_TIME;
break;
default:
return IPMI_CC_SYSTEM_INFO_PARAMETER_NOT_SUPPORTED;
break;
}
return IPMI_CC_OK;
}
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
ipmiPrintAndRegister(NETFUN_APP, CMD_APP_SET_SYS_INFO_PARAMS, NULL,
ipmiAppSetSysInfoParams,
PRIVILEGE_USER); // Set Sys Info Params
ipmiPrintAndRegister(NETFUN_APP, CMD_APP_GET_SYS_INFO_PARAMS, NULL,
ipmiAppGetSysInfoParams,
PRIVILEGE_USER); // Get Sys Info Params
return;
}
} // namespace ipmi