blob: af2171352d6571553122ce7a3360f5e48a6b91db [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 "xyz/openbmc_project/Common/error.hpp"
#include <ipmid/api.h>
#include <array>
#include <commandutils.hpp>
#include <cstring>
#include <iostream>
#include <oemcommands.hpp>
#include <ipmid/utils.hpp>
#include <phosphor-logging/log.hpp>
#include <sdbusplus/bus.hpp>
#include <string>
#include <vector>
#define SIZE_IANA_ID 3
namespace ipmi
{
static void registerOEMFunctions() __attribute__((constructor));
sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); // from ipmid/api.h
static constexpr size_t maxFRUStringLength = 0x3F;
ipmi_ret_t plat_udbg_get_post_desc(uint8_t, uint8_t *, uint8_t, uint8_t *,
uint8_t *, uint8_t *);
ipmi_ret_t plat_udbg_get_frame_data(uint8_t, uint8_t, uint8_t *, uint8_t *,
uint8_t *);
ipmi_ret_t plat_udbg_control_panel(uint8_t, uint8_t, uint8_t, uint8_t *,
uint8_t *);
namespace variant_ns = sdbusplus::message::variant_ns;
enum class LanParam : uint8_t
{
INPROGRESS = 0,
AUTHSUPPORT = 1,
AUTHENABLES = 2,
IP = 3,
IPSRC = 4,
MAC = 5,
SUBNET = 6,
GATEWAY = 12,
VLAN = 20,
CIPHER_SUITE_COUNT = 22,
CIPHER_SUITE_ENTRIES = 23,
IPV6 = 59,
};
ipmi_ret_t getNetworkData(uint8_t lan_param, char *data)
{
ipmi_ret_t rc = IPMI_CC_OK;
sdbusplus::bus::bus bus(ipmid_get_sd_bus_connection());
const std::string ethdevice = "eth0";
switch (static_cast<LanParam>(lan_param))
{
case LanParam::IP:
{
auto ethIP = ethdevice + "/" + ipmi::network::IP_TYPE;
std::string ipaddress;
auto ipObjectInfo = ipmi::getIPObject(
bus, ipmi::network::IP_INTERFACE, ipmi::network::ROOT, ethIP);
auto properties = ipmi::getAllDbusProperties(
bus, ipObjectInfo.second, ipObjectInfo.first,
ipmi::network::IP_INTERFACE);
ipaddress = variant_ns::get<std::string>(properties["Address"]);
std::strcpy(data, ipaddress.c_str());
}
break;
case LanParam::IPV6:
{
auto ethIP = ethdevice + "/ipv6";
std::string ipaddress;
auto ipObjectInfo = ipmi::getIPObject(
bus, ipmi::network::IP_INTERFACE, ipmi::network::ROOT, ethIP);
auto properties = ipmi::getAllDbusProperties(
bus, ipObjectInfo.second, ipObjectInfo.first,
ipmi::network::IP_INTERFACE);
ipaddress = variant_ns::get<std::string>(properties["Address"]);
std::strcpy(data, ipaddress.c_str());
}
break;
case LanParam::MAC:
{
std::string macAddress;
auto macObjectInfo =
ipmi::getDbusObject(bus, ipmi::network::MAC_INTERFACE,
ipmi::network::ROOT, ethdevice);
auto variant = ipmi::getDbusProperty(
bus, macObjectInfo.second, macObjectInfo.first,
ipmi::network::MAC_INTERFACE, "MACAddress");
macAddress = variant_ns::get<std::string>(variant);
sscanf(macAddress.c_str(), ipmi::network::MAC_ADDRESS_FORMAT,
(data), (data + 1), (data + 2), (data + 3), (data + 4),
(data + 5));
std::strcpy(data, macAddress.c_str());
}
break;
default:
rc = IPMI_CC_PARM_OUT_OF_RANGE;
}
return rc;
}
// return code: 0 successful
int8_t getFruData(std::string &data, std::string &name)
{
std::string objpath = "/xyz/openbmc_project/FruDevice";
std::string intf = "xyz.openbmc_project.FruDeviceManager";
std::string service = getService(dbus, intf, objpath);
ObjectValueTree valueTree = getManagedObjects(dbus, service, "/");
if (valueTree.empty())
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"No object implements interface",
phosphor::logging::entry("INTF=%s", intf.c_str()));
return -1;
}
for (const auto &item : valueTree)
{
auto interface = item.second.find("xyz.openbmc_project.FruDevice");
if (interface == item.second.end())
{
continue;
}
auto property = interface->second.find(name.c_str());
if (property == interface->second.end())
{
continue;
}
try
{
Value variant = property->second;
std::string &result =
sdbusplus::message::variant_ns::get<std::string>(variant);
if (result.size() > maxFRUStringLength)
{
phosphor::logging::log<phosphor::logging::level::ERR>(
"FRU serial number exceed maximum length");
return -1;
}
data = result;
return 0;
}
catch (sdbusplus::message::variant_ns::bad_variant_access &e)
{
phosphor::logging::log<phosphor::logging::level::ERR>(e.what());
return -1;
}
}
return -1;
}
typedef struct
{
uint8_t cur_power_state;
uint8_t last_power_event;
uint8_t misc_power_state;
uint8_t front_panel_button_cap_status;
} ipmi_get_chassis_status_t;
// Todo: Needs to update this as per power policy when integrated
//----------------------------------------------------------------------
// Get Chassis Status commands
//----------------------------------------------------------------------
ipmi_ret_t ipmiGetChassisStatus(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len,
ipmi_context_t context)
{
ipmi_get_chassis_status_t chassis_status;
uint8_t s = 2;
*data_len = 4;
// Current Power State
// [7] reserved
// [6..5] power restore policy
// 00b = chassis stays powered off after AC/mains returns
// 01b = after AC returns, power is restored to the state that was
// in effect when AC/mains was lost.
// 10b = chassis always powers up after AC/mains returns
// 11b = unknow
// Set to 00b, by observing the hardware behavior.
// Do we need to define a dbus property to identify the restore
// policy?
// [4] power control fault
// 1b = controller attempted to turn system power on or off, but
// system did not enter desired state.
// Set to 0b, since We don't support it..
// [3] power fault
// 1b = fault detected in main power subsystem.
// set to 0b. for we don't support it.
// [2] 1b = interlock (chassis is presently shut down because a chassis
// panel interlock switch is active). (IPMI 1.5)
// set to 0b, for we don't support it.
// [1] power overload
// 1b = system shutdown because of power overload condition.
// set to 0b, for we don't support it.
// [0] power is on
// 1b = system power is on
// 0b = system power is off(soft-off S4/S5, or mechanical off)
chassis_status.cur_power_state = ((s & 0x3) << 5) | (1 & 0x1);
// Last Power Event
// [7..5] – reserved
// [4] – 1b = last ‘Power is on’ state was entered via IPMI command
// [3] – 1b = last power down caused by power fault
// [2] – 1b = last power down caused by a power interlock being activated
// [1] – 1b = last power down caused by a Power overload
// [0] – 1b = AC failed
// set to 0x0, for we don't support these fields.
chassis_status.last_power_event = 0;
// Misc. Chassis State
// [7] – reserved
// [6] – 1b = Chassis Identify command and state info supported (Optional)
// 0b = Chassis Identify command support unspecified via this command.
// (The Get Command Support command , if implemented, would still
// indicate support for the Chassis Identify command)
// [5..4] – Chassis Identify State. Mandatory when bit[6] =1b, reserved
// (return
// as 00b) otherwise. Returns the present chassis identify state.
// Refer to the Chassis Identify command for more info.
// 00b = chassis identify state = Off
// 01b = chassis identify state = Temporary(timed) On
// 10b = chassis identify state = Indefinite On
// 11b = reserved
// [3] – 1b = Cooling/fan fault detected
// [2] – 1b = Drive Fault
// [1] – 1b = Front Panel Lockout active (power off and reset via chassis
// push-buttons disabled.)
// [0] – 1b = Chassis Intrusion active
// set to 0, for we don't support them.
chassis_status.misc_power_state = 0x40;
// Front Panel Button Capabilities and disable/enable status(Optional)
// set to 0, for we don't support them.
chassis_status.front_panel_button_cap_status = 0;
// Pack the actual response
std::memcpy(response, &chassis_status, *data_len);
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Get Debug Frame Info
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemDbgGetFrameInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len,
ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
uint8_t num_frames = 3;
std::memcpy(res, req, SIZE_IANA_ID); // IANA ID
res[SIZE_IANA_ID] = num_frames;
*data_len = SIZE_IANA_ID + 1;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Get Debug Updated Frames
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemDbgGetUpdFrames(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len,
ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
uint8_t num_updates = 3;
*data_len = 4;
std::memcpy(res, req, SIZE_IANA_ID); // IANA ID
res[SIZE_IANA_ID] = num_updates;
*data_len = SIZE_IANA_ID + num_updates + 1;
res[SIZE_IANA_ID + 1] = 1; // info page update
res[SIZE_IANA_ID + 2] = 2; // cri sel update
res[SIZE_IANA_ID + 3] = 3; // cri sensor update
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Get Debug POST Description
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemDbgGetPostDesc(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len,
ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
uint8_t index = 0;
uint8_t next = 0;
uint8_t end = 0;
uint8_t phase = 0;
uint8_t count = 0;
int ret;
index = req[3];
phase = req[4];
phosphor::logging::log<phosphor::logging::level::INFO>(
"Get POST Description Event");
ret = plat_udbg_get_post_desc(index, &next, phase, &end, &count, &res[8]);
if (ret)
{
memcpy(res, req, SIZE_IANA_ID); // IANA ID
*data_len = SIZE_IANA_ID;
return IPMI_CC_UNSPECIFIED_ERROR;
}
memcpy(res, req, SIZE_IANA_ID); // IANA ID
res[3] = index;
res[4] = next;
res[5] = phase;
res[6] = end;
res[7] = count;
*data_len = SIZE_IANA_ID + 5 + count;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Get Debug GPIO Description
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemDbgGetGpioDesc(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len,
ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
phosphor::logging::log<phosphor::logging::level::INFO>(
"Get GPIO Description Event");
std::memcpy(res, req, SIZE_IANA_ID + 1); // IANA ID
*data_len = SIZE_IANA_ID + 1;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Get Debug Frame Data
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemDbgGetFrameData(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len,
ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
uint8_t frame;
uint8_t page;
uint8_t next;
uint8_t count;
int ret;
frame = req[3];
page = req[4];
int fr = frame;
int pg = page;
ret = plat_udbg_get_frame_data(frame, page, &next, &count, &res[7]);
if (ret)
{
memcpy(res, req, SIZE_IANA_ID); // IANA ID
*data_len = SIZE_IANA_ID;
return IPMI_CC_UNSPECIFIED_ERROR;
}
memcpy(res, req, SIZE_IANA_ID); // IANA ID
res[3] = frame;
res[4] = page;
res[5] = next;
res[6] = count;
*data_len = SIZE_IANA_ID + 4 + count;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Get Debug Control Panel
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemDbgGetCtrlPanel(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len,
ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
uint8_t panel;
uint8_t operation;
uint8_t item;
uint8_t count;
ipmi_ret_t ret;
panel = req[3];
operation = req[4];
item = req[5];
ret = plat_udbg_control_panel(panel, operation, item, &count, &res[3]);
std::memcpy(res, req, SIZE_IANA_ID); // IANA ID
*data_len = SIZE_IANA_ID + count;
return ret;
}
// Todo: Need to implement all below functions for oem commands
//----------------------------------------------------------------------
// Set Dimm Info (CMD_OEM_SET_DIMM_INFO)
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemSetDimmInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request, ipmi_response_t response,
ipmi_data_len_t data_len, ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
std::memcpy(res, req, SIZE_IANA_ID + 1); // IANA ID
*data_len = SIZE_IANA_ID + 1;
*data_len = 0;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Get Boot Order (CMD_OEM_GET_BOOT_ORDER)
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemGetBootOrder(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request, ipmi_response_t response,
ipmi_data_len_t data_len, ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
*res++ = 0x01;
*res++ = 0x00;
*res++ = 0x09;
*res++ = 0x02;
*res++ = 0x03;
*res++ = 0xff;
*data_len = 6;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Set Machine Config Info (CMD_OEM_SET_MACHINE_CONFIG_INFO)
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemSetMachineCfgInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len,
ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
*data_len = 0;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Set POST start (CMD_OEM_SET_POST_START)
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemSetPostStart(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request, ipmi_response_t response,
ipmi_data_len_t data_len, ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
phosphor::logging::log<phosphor::logging::level::INFO>("POST Start Event");
*data_len = 0;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Set POST End (CMD_OEM_SET_POST_END)
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemSetPostEnd(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request, ipmi_response_t response,
ipmi_data_len_t data_len, ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
phosphor::logging::log<phosphor::logging::level::INFO>("POST End Event");
*data_len = 0;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Set Bios Flash Info (CMD_OEM_SET_BIOS_FLASH_INFO)
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemSetBiosFlashInfo(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request,
ipmi_response_t response,
ipmi_data_len_t data_len,
ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
*data_len = 0;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Set PPR (CMD_OEM_SET_PPR)
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemSetPpr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request, ipmi_response_t response,
ipmi_data_len_t data_len, ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
*data_len = 0;
return IPMI_CC_OK;
}
//----------------------------------------------------------------------
// Get PPR (CMD_OEM_GET_PPR)
//----------------------------------------------------------------------
ipmi_ret_t ipmiOemGetPpr(ipmi_netfn_t netfn, ipmi_cmd_t cmd,
ipmi_request_t request, ipmi_response_t response,
ipmi_data_len_t data_len, ipmi_context_t context)
{
uint8_t *req = reinterpret_cast<uint8_t *>(request);
uint8_t *res = reinterpret_cast<uint8_t *>(response);
res[0] = 0x00;
*data_len = 1;
return IPMI_CC_OK;
}
static void registerOEMFunctions(void)
{
phosphor::logging::log<phosphor::logging::level::INFO>(
"Registering OEM commands");
ipmiPrintAndRegister(NETFUN_CHASSIS, 1, NULL, ipmiGetChassisStatus,
PRIVILEGE_USER); // get chassis status
ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_FRAME_INFO,
NULL, ipmiOemDbgGetFrameInfo,
PRIVILEGE_USER); // get debug frame info
ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ,
CMD_OEM_USB_DBG_GET_UPDATED_FRAMES, NULL,
ipmiOemDbgGetUpdFrames,
PRIVILEGE_USER); // get debug updated frames
ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_POST_DESC,
NULL, ipmiOemDbgGetPostDesc,
PRIVILEGE_USER); // get debug post description
ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_GPIO_DESC,
NULL, ipmiOemDbgGetGpioDesc,
PRIVILEGE_USER); // get debug gpio description
ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_GET_FRAME_DATA,
NULL, ipmiOemDbgGetFrameData,
PRIVILEGE_USER); // get debug frame data
ipmiPrintAndRegister(NETFN_OEM_USB_DBG_REQ, CMD_OEM_USB_DBG_CTRL_PANEL,
NULL, ipmiOemDbgGetCtrlPanel,
PRIVILEGE_USER); // get debug control panel
ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_DIMM_INFO, NULL,
ipmiOemSetDimmInfo,
PRIVILEGE_USER); // Set Dimm Info
ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_GET_BOOT_ORDER, NULL,
ipmiOemGetBootOrder,
PRIVILEGE_USER); // Get Boot Order
ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_MACHINE_CONFIG_INFO, NULL,
ipmiOemSetMachineCfgInfo,
PRIVILEGE_USER); // Set Machine Config Info
ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_POST_START, NULL,
ipmiOemSetPostStart,
PRIVILEGE_USER); // Set POST start
ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_POST_END, NULL,
ipmiOemSetPostEnd,
PRIVILEGE_USER); // Set POST End
ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_BIOS_FLASH_INFO, NULL,
ipmiOemSetBiosFlashInfo,
PRIVILEGE_USER); // Set Bios Flash Info
ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_SET_PPR, NULL, ipmiOemSetPpr,
PRIVILEGE_USER); // Set PPR
ipmiPrintAndRegister(NETFUN_NONE, CMD_OEM_GET_PPR, NULL, ipmiOemGetPpr,
PRIVILEGE_USER); // Get PPR
return;
}
} // namespace ipmi