1177 lines
32 KiB
C
1177 lines
32 KiB
C
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
|
// Copyright 2022 IBM Corp.
|
|
|
|
#define pr_fmt(fmt) "PLDM: " fmt
|
|
|
|
#include <bitmap.h>
|
|
#include <cpu.h>
|
|
#include <opal.h>
|
|
#include <opal-msg.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <debug_descriptor.h>
|
|
#include <libpldm/fru.h>
|
|
#include <libpldm/platform.h>
|
|
#include <libpldm/platform_oem_ibm.h>
|
|
#include <libpldm/state_set.h>
|
|
#include <libpldm/utils.h>
|
|
#include "pldm.h"
|
|
|
|
struct pldm_type {
|
|
const char *name;
|
|
int pldm_type_id;
|
|
ver32_t version;
|
|
|
|
struct list_head commands;
|
|
struct list_node link;
|
|
};
|
|
|
|
struct pldm_cmd {
|
|
const char *name;
|
|
int pldm_cmd_id;
|
|
|
|
int (*handler)(const struct pldm_rx_data *rx);
|
|
|
|
struct list_node link; /* link in the msg type's command list */
|
|
};
|
|
|
|
/*
|
|
* Send a response with just a completion code and no payload
|
|
*/
|
|
static int cc_resp(const struct pldm_rx_data *rx, uint8_t type,
|
|
uint8_t command, uint8_t cc)
|
|
{
|
|
size_t data_size = PLDM_MSG_SIZE(uint8_t);
|
|
struct pldm_tx_data *tx;
|
|
int rc;
|
|
|
|
/* Encode the cc response */
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
encode_cc_only_resp(rx->hdrinf.instance,
|
|
type,
|
|
command,
|
|
cc,
|
|
(struct pldm_msg *)tx->data);
|
|
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send response message containing only cc, "
|
|
"rc = %d, cc = %d\n", rc, cc);
|
|
free(tx);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
/*
|
|
* PLDM Type / Command wrangling.
|
|
*/
|
|
LIST_HEAD(pldm_type_list);
|
|
|
|
static const struct pldm_type *find_type(int type_id)
|
|
{
|
|
struct pldm_type *iter;
|
|
|
|
list_for_each(&pldm_type_list, iter, link) {
|
|
if (iter->pldm_type_id == type_id)
|
|
return iter;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static const struct pldm_cmd *find_cmd(const struct pldm_type *type, int cmd)
|
|
{
|
|
struct pldm_cmd *iter;
|
|
|
|
list_for_each(&type->commands, iter, link)
|
|
if (iter->pldm_cmd_id == cmd)
|
|
return iter;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void add_type(struct pldm_type *new_type)
|
|
{
|
|
assert(new_type->pldm_type_id < 32); /* limited by GetPLDMTypes */
|
|
assert(!find_type(new_type->pldm_type_id));
|
|
|
|
list_head_init(&new_type->commands);
|
|
list_add_tail(&pldm_type_list, &new_type->link);
|
|
|
|
prlog(PR_DEBUG, "Registered type %s (%d)\n",
|
|
new_type->name, new_type->pldm_type_id);
|
|
}
|
|
|
|
static void add_cmd(struct pldm_type *type, struct pldm_cmd *new_cmd)
|
|
{
|
|
assert(new_cmd->pldm_cmd_id < 256); /* limited by GetPLDMCommands */
|
|
assert(new_cmd->handler);
|
|
assert(!find_cmd(type, new_cmd->pldm_cmd_id));
|
|
|
|
list_add_tail(&type->commands, &new_cmd->link);
|
|
prlog(PR_DEBUG, "Registered command %s (%d) under %s\n",
|
|
new_cmd->name, new_cmd->pldm_cmd_id, type->name);
|
|
}
|
|
|
|
/*
|
|
* PLDM Base commands support
|
|
*/
|
|
static struct pldm_type pldm_base_type = {
|
|
.name = "base",
|
|
.pldm_type_id = PLDM_BASE,
|
|
.version = { 0xF1, 0xF0, 0xF0, 0x00 },
|
|
};
|
|
|
|
/*
|
|
* GetTID command (0x02)
|
|
* The GetTID command is used to retrieve the present Terminus ID (TID)
|
|
* setting for a PLDM Terminus.
|
|
*/
|
|
static int base_get_tid_handler(const struct pldm_rx_data *rx)
|
|
{
|
|
size_t data_size = PLDM_MSG_SIZE(struct pldm_get_tid_resp);
|
|
struct pldm_tx_data *tx;
|
|
int rc;
|
|
|
|
/* create a PLDM response message for GetTID */
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
rc = encode_get_tid_resp(rx->hdrinf.instance,
|
|
PLDM_SUCCESS,
|
|
HOST_TID,
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode GetTID Error, rc: %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send GetTID response, rc = %d\n", rc);
|
|
free(tx);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_base_get_tid = {
|
|
.name = "PLDM_GET_TID",
|
|
.pldm_cmd_id = PLDM_GET_TID,
|
|
.handler = base_get_tid_handler,
|
|
};
|
|
|
|
/*
|
|
* GetPLDMTypes (0x04)
|
|
* The GetPLDMTypes command can be used to discover the PLDM type
|
|
* capabilities supported by a PLDM terminus and to get a list of the
|
|
* PLDM types that are supported.
|
|
*/
|
|
static int base_get_types_handler(const struct pldm_rx_data *rx)
|
|
{
|
|
size_t data_size = PLDM_MSG_SIZE(struct pldm_get_types_resp);
|
|
bitmap_elem_t type_map[BITMAP_ELEMS(PLDM_MAX_TYPES)];
|
|
struct pldm_tx_data *tx;
|
|
struct pldm_type *iter;
|
|
int rc;
|
|
|
|
/* build the supported type list from the registered type
|
|
* handlers
|
|
*/
|
|
memset(type_map, 0, sizeof(type_map));
|
|
list_for_each(&pldm_type_list, iter, link)
|
|
bitmap_set_bit(type_map, iter->pldm_type_id);
|
|
|
|
for (int i = 0; i < BITMAP_ELEMS(PLDM_MAX_TYPES); i++)
|
|
type_map[i] = cpu_to_le64(type_map[i]);
|
|
|
|
/* create a PLDM response message for GetPLDMTypes */
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
rc = encode_get_types_resp(rx->hdrinf.instance,
|
|
PLDM_SUCCESS,
|
|
(bitfield8_t *)type_map,
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode GetPLDMTypes Error, rc: %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send GetPLDMTypes response, rc = %d\n", rc);
|
|
free(tx);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_base_get_types = {
|
|
.name = "PLDM_GET_PLDM_TYPES",
|
|
.pldm_cmd_id = PLDM_GET_PLDM_TYPES,
|
|
.handler = base_get_types_handler,
|
|
};
|
|
|
|
/*
|
|
* Extended error codes defined for the Base command set.
|
|
*/
|
|
#define INVALID_DATA_TRANSFER_HANDLE 0x80
|
|
#define INVALID_TRANSFER_OPERATION_FLAG 0x81
|
|
#define INVALID_PLDM_TYPE_IN_REQUEST_DATA 0x83
|
|
#define INVALID_PLDM_VERSION_IN_REQUEST_DATA 0x84
|
|
|
|
/*
|
|
* GetPLDMCommands (0x05)
|
|
* The GetPLDMCommands command can be used to discover the PLDM command
|
|
* capabilities supported by aPLDM terminus for a specific PLDM Type and
|
|
* version as a responder.
|
|
*/
|
|
static int base_get_commands_handler(const struct pldm_rx_data *rx)
|
|
{
|
|
size_t data_size = PLDM_MSG_SIZE(struct pldm_get_commands_resp);
|
|
bitmap_elem_t cmd_map[BITMAP_ELEMS(PLDM_MAX_CMDS_PER_TYPE)];
|
|
const struct pldm_type *type;
|
|
const struct pldm_cmd *iter;
|
|
struct pldm_tx_data *tx;
|
|
size_t payload_len;
|
|
ver32_t version;
|
|
uint8_t type_id;
|
|
int rc;
|
|
|
|
payload_len = rx->msg_len - sizeof(struct pldm_msg_hdr);
|
|
rc = decode_get_commands_req(rx->msg, payload_len,
|
|
&type_id, &version);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to decode GetPLDMCommands request, rc = %d", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
return OPAL_INTERNAL_ERROR;
|
|
}
|
|
|
|
type = find_type(type_id);
|
|
if (!type) {
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
INVALID_PLDM_TYPE_IN_REQUEST_DATA);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
if (memcmp(&type->version, &version, sizeof(version))) {
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
INVALID_PLDM_VERSION_IN_REQUEST_DATA);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* build the supported type list from the registered type
|
|
* handlers
|
|
*/
|
|
memset(cmd_map, 0, sizeof(cmd_map));
|
|
list_for_each(&type->commands, iter, link)
|
|
bitmap_set_bit(cmd_map, iter->pldm_cmd_id);
|
|
|
|
/* fix the endian */
|
|
for (int i = 0; i < BITMAP_ELEMS(PLDM_MAX_CMDS_PER_TYPE); i++)
|
|
cmd_map[i] = cpu_to_le64(cmd_map[i]);
|
|
|
|
/* create a PLDM response message for GetPLDMCommands */
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
rc = encode_get_commands_resp(rx->hdrinf.instance,
|
|
PLDM_SUCCESS,
|
|
(bitfield8_t *)cmd_map,
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode GetPLDMCommands Error, rc: %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* send PLDM message over MCTP */
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send GetPLDMCommands response, rc = %d\n", rc);
|
|
return OPAL_HARDWARE;
|
|
free(tx);
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_base_get_commands = {
|
|
.name = "PLDM_GET_PLDM_COMMANDS",
|
|
.pldm_cmd_id = PLDM_GET_PLDM_COMMANDS,
|
|
.handler = base_get_commands_handler,
|
|
};
|
|
|
|
/*
|
|
* GetPLDMVersion (0x03)
|
|
* The GetPLDMVersion command can be used to retrieve the PLDM base
|
|
* specification versions that the PLDM terminus supports, as well as
|
|
* the PLDM Type specification versions supported for each PLDM Type.
|
|
*/
|
|
static int base_get_version_handler(const struct pldm_rx_data *rx)
|
|
{
|
|
uint32_t version_data[2];
|
|
size_t data_size = PLDM_MSG_SIZE(struct pldm_get_version_resp) + sizeof(version_data);
|
|
const struct pldm_type *type;
|
|
struct pldm_tx_data *tx;
|
|
uint8_t type_id, opflag;
|
|
uint32_t xfer_handle;
|
|
size_t payload_len;
|
|
int rc;
|
|
|
|
payload_len = rx->msg_len - sizeof(struct pldm_msg_hdr);
|
|
rc = decode_get_version_req(rx->msg, payload_len,
|
|
&xfer_handle,
|
|
&opflag,
|
|
&type_id);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to decode GetPLDMVersion request, rc = %d", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
return OPAL_INTERNAL_ERROR;
|
|
}
|
|
|
|
/* reject multipart requests */
|
|
if (opflag != PLDM_GET_FIRSTPART) {
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
INVALID_TRANSFER_OPERATION_FLAG);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
type = find_type(type_id);
|
|
if (!type) {
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
INVALID_PLDM_TYPE_IN_REQUEST_DATA);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* pack a scratch buffer with our version(s) and CRC32 the lot */
|
|
memcpy(&version_data[0], &type->version, 4);
|
|
|
|
version_data[1] = cpu_to_le32(crc32(&type->version, 4));
|
|
|
|
/* create a PLDM response for GetPLDMVersion */
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
rc = encode_get_version_resp(rx->hdrinf.instance,
|
|
PLDM_SUCCESS,
|
|
0x0, /* no handle */
|
|
PLDM_START_AND_END,
|
|
(ver32_t *) version_data,
|
|
sizeof(version_data),
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode GetPLDMVersion Error, rc: %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* send PLDM message over MCTP */
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send GetPLDMVersion response, rc = %d\n", rc);
|
|
free(tx);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
free(tx);
|
|
|
|
/* BMC has certainly rebooted, so reload the PDRs */
|
|
return pldm_platform_reload_pdrs();
|
|
}
|
|
|
|
static struct pldm_cmd pldm_base_get_version = {
|
|
.name = "PLDM_GET_PLDM_VERSION",
|
|
.pldm_cmd_id = PLDM_GET_PLDM_VERSION,
|
|
.handler = base_get_version_handler,
|
|
};
|
|
|
|
/*
|
|
* PLDM Platform commands support
|
|
*/
|
|
static struct pldm_type pldm_platform_type = {
|
|
.name = "platform",
|
|
.pldm_type_id = PLDM_PLATFORM,
|
|
};
|
|
|
|
#define MIN_WATCHDOG_TIMEOUT_SEC 15
|
|
|
|
/*
|
|
* SetEventReceiver (0x04)
|
|
* The SetEventReceiver command is used to set the address of the Event
|
|
* Receiver into a terminus that generates event messages. It is also
|
|
* used to globally enable or disable whether event messages are
|
|
* generated from the terminus.
|
|
*/
|
|
static int platform_set_event_receiver_handler(const struct pldm_rx_data *rx)
|
|
{
|
|
uint8_t event_message_global_enable, transport_protocol_type;
|
|
uint8_t event_receiver_address_info, cc = PLDM_SUCCESS;
|
|
uint16_t heartbeat_timer;
|
|
int rc = OPAL_SUCCESS;
|
|
|
|
/* decode SetEventReceiver request data */
|
|
rc = decode_set_event_receiver_req(
|
|
rx->msg,
|
|
PLDM_SET_EVENT_RECEIVER_REQ_BYTES,
|
|
&event_message_global_enable,
|
|
&transport_protocol_type,
|
|
&event_receiver_address_info,
|
|
&heartbeat_timer);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to decode SetEventReceiver request, rc = %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
return OPAL_INTERNAL_ERROR;
|
|
}
|
|
|
|
/* invoke the appropriate callback handler */
|
|
prlog(PR_DEBUG, "%s - event_message_global_enable: %d, "
|
|
"transport_protocol_type: %d "
|
|
"event_receiver_address_info: %d "
|
|
"heartbeat_timer: %d\n",
|
|
__func__,
|
|
event_message_global_enable,
|
|
transport_protocol_type,
|
|
event_receiver_address_info,
|
|
heartbeat_timer);
|
|
|
|
if (event_message_global_enable !=
|
|
PLDM_EVENT_MESSAGE_GLOBAL_ENABLE_ASYNC_KEEP_ALIVE) {
|
|
|
|
prlog(PR_ERR, "%s - invalid value for message global enable received: %d\n",
|
|
__func__, event_message_global_enable);
|
|
cc = PLDM_PLATFORM_ENABLE_METHOD_NOT_SUPPORTED;
|
|
}
|
|
|
|
if (heartbeat_timer < MIN_WATCHDOG_TIMEOUT_SEC) {
|
|
prlog(PR_ERR, "%s - BMC requested watchdog timeout that's too small: %d\n",
|
|
__func__, heartbeat_timer);
|
|
cc = PLDM_PLATFORM_HEARTBEAT_FREQUENCY_TOO_HIGH;
|
|
} else {
|
|
/* set the internal watchdog period to what BMC indicated */
|
|
watchdog_period_sec = heartbeat_timer;
|
|
}
|
|
|
|
/* send the response to BMC */
|
|
cc_resp(rx, PLDM_PLATFORM, PLDM_SET_EVENT_RECEIVER, cc);
|
|
|
|
/* no error happened above, so arm the watchdog and set the default timeout */
|
|
if (cc == PLDM_SUCCESS)
|
|
watchdog_armed = true;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_platform_set_event_receiver = {
|
|
.name = "PLDM_SET_EVENT_RECEIVER",
|
|
.pldm_cmd_id = PLDM_SET_EVENT_RECEIVER,
|
|
.handler = platform_set_event_receiver_handler,
|
|
};
|
|
|
|
/*
|
|
* PlatformEventMessage (0x10)
|
|
* PLDM Event Messages are sent as PLDM request messages to the Event
|
|
* Receiver using the PlatformEventMessage command.
|
|
*/
|
|
static int platform_event_message(const struct pldm_rx_data *rx)
|
|
{
|
|
size_t data_size = PLDM_MSG_SIZE(struct pldm_platform_event_message_resp);
|
|
struct pldm_bios_attribute_update_event_req *request;
|
|
uint8_t format_version, tid, event_class;
|
|
uint8_t *bios_attribute_handles;
|
|
uint8_t cc = PLDM_SUCCESS;
|
|
size_t event_data_offset;
|
|
struct pldm_tx_data *tx;
|
|
int rc, i;
|
|
|
|
/* decode PlatformEventMessage request data */
|
|
rc = decode_platform_event_message_req(
|
|
rx->msg,
|
|
sizeof(struct pldm_platform_event_message_req),
|
|
&format_version,
|
|
&tid,
|
|
&event_class,
|
|
&event_data_offset);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to decode PlatformEventMessage request, rc = %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
return OPAL_INTERNAL_ERROR;
|
|
}
|
|
|
|
prlog(PR_DEBUG, "%s - format_version: %d, "
|
|
"tid: %d "
|
|
"event_class: %d "
|
|
"event_data: 0x%lx\n",
|
|
__func__,
|
|
format_version, tid,
|
|
event_class, event_data_offset);
|
|
|
|
/* we don't support any other event than the PDR Repo Changed event */
|
|
if ((event_class != PLDM_PDR_REPOSITORY_CHG_EVENT) &&
|
|
(event_class != PLDM_EVENT_TYPE_OEM_EVENT_BIOS_ATTRIBUTE_UPDATE)) {
|
|
prlog(PR_ERR, "%s - Invalid event class %d in platform event handler\n",
|
|
__func__, event_class);
|
|
cc = PLDM_ERROR;
|
|
}
|
|
|
|
/* Encode the platform event request */
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
rc = encode_platform_event_message_resp(
|
|
rx->hdrinf.instance,
|
|
cc,
|
|
PLDM_EVENT_NO_LOGGING,
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode PlatformEventMessage Error, rc: %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* send PLDM message over MCTP */
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send PlatformEventMessage response, rc = %d\n", rc);
|
|
free(tx);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
/* invoke the appropriate callback handler */
|
|
if (event_class == PLDM_PDR_REPOSITORY_CHG_EVENT) {
|
|
free(tx);
|
|
return pldm_platform_reload_pdrs();
|
|
}
|
|
|
|
/* When the attribute value changes for any BIOS attribute, then
|
|
* PlatformEventMessage command with OEM event type
|
|
* PLDM_EVENT_TYPE_OEM_EVENT_BIOS_ATTRIBUTE_UPDATE is send to
|
|
* host with the list of BIOS attribute handles.
|
|
*/
|
|
if (event_class == PLDM_EVENT_TYPE_OEM_EVENT_BIOS_ATTRIBUTE_UPDATE) {
|
|
request = (struct pldm_bios_attribute_update_event_req *)rx->msg->payload;
|
|
bios_attribute_handles = (uint8_t *)request->bios_attribute_handles;
|
|
|
|
prlog(PR_DEBUG, "%s - OEM_EVENT_BIOS_ATTRIBUTE_UPDATE, handles: %d\n",
|
|
__func__, request->num_handles);
|
|
|
|
/* list of BIOS attribute handles */
|
|
for (i = 0; i < request->num_handles; i++) {
|
|
prlog(PR_DEBUG, "%s - OEM_EVENT_BIOS_ATTRIBUTE_UPDATE: handle(%d): %d\n",
|
|
__func__, i, *bios_attribute_handles);
|
|
bios_attribute_handles += sizeof(uint16_t);
|
|
}
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_platform_event_message = {
|
|
.name = "PLDM_PLATFORM_EVENT_MESSAGE",
|
|
.pldm_cmd_id = PLDM_PLATFORM_EVENT_MESSAGE,
|
|
.handler = platform_event_message,
|
|
};
|
|
|
|
/*
|
|
* GetStateSensorReadings (0x21)
|
|
* The GetStateSensorReadings command can return readings for multiple
|
|
* state sensors (a PLDM State Sensor that returns more than one set of
|
|
* state information is called a composite state sensor).
|
|
*/
|
|
static int platform_get_state_sensor_readings(const struct pldm_rx_data *rx)
|
|
{
|
|
bitfield8_t sensor_rearm;
|
|
struct pldm_tx_data *tx;
|
|
uint16_t sensor_id;
|
|
uint8_t reserved;
|
|
size_t data_size;
|
|
int rc;
|
|
|
|
get_sensor_state_field sensor_state = {
|
|
.sensor_op_state = PLDM_SENSOR_UNKNOWN,
|
|
.present_state = 0,
|
|
.previous_state = 0,
|
|
.event_state = 0
|
|
};
|
|
|
|
/* decode GetStateSensorReadings request data */
|
|
rc = decode_get_state_sensor_readings_req(
|
|
rx->msg,
|
|
PLDM_GET_STATE_SENSOR_READINGS_REQ_BYTES,
|
|
&sensor_id,
|
|
&sensor_rearm,
|
|
&reserved);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to decode GetStateSensorReadings request, rc = %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
return OPAL_INTERNAL_ERROR;
|
|
}
|
|
|
|
prlog(PR_DEBUG, "%s - sensor_id: %d, sensor_rearm: %x\n",
|
|
__func__, sensor_id, sensor_rearm.byte);
|
|
|
|
/* send state sensor reading response */
|
|
data_size = sizeof(struct pldm_msg_hdr) +
|
|
sizeof(struct pldm_get_state_sensor_readings_resp) +
|
|
(sizeof(get_sensor_state_field) * 1);
|
|
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
rc = encode_get_state_sensor_readings_resp(
|
|
rx->hdrinf.instance,
|
|
PLDM_SUCCESS,
|
|
1, /* sensor count of 1 */
|
|
&sensor_state,
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode GetStateSensorReadings response Error, rc: %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* send PLDM message over MCTP */
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send GetStateSensorReadings response, rc = %d\n", rc);
|
|
free(tx);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_platform_get_state_sensor_readings = {
|
|
.name = "PLDM_GET_STATE_SENSOR_READINGS",
|
|
.pldm_cmd_id = PLDM_GET_STATE_SENSOR_READINGS,
|
|
.handler = platform_get_state_sensor_readings,
|
|
};
|
|
|
|
#define SOFT_OFF 0x00
|
|
#define SOFT_REBOOT 0x01
|
|
#define CHASSIS_PWR_DOWN 0x00
|
|
#define DEFAULT_CHIP_ID 0
|
|
|
|
/*
|
|
* SetStateEffecterStates (0x39)
|
|
* The SetStateEffecterStates command is used to set the state of one
|
|
* or more effecters within a PLDM State Effecter.
|
|
*/
|
|
static int platform_set_state_effecter_states_handler(const struct pldm_rx_data *rx)
|
|
{
|
|
set_effecter_state_field field[8];
|
|
uint8_t comp_effecter_count;
|
|
uint16_t effecter_id;
|
|
int rc, i;
|
|
|
|
/* decode SetStateEffecterStates request data */
|
|
rc = decode_set_state_effecter_states_req(
|
|
rx->msg,
|
|
PLDM_SET_STATE_EFFECTER_STATES_REQ_BYTES,
|
|
&effecter_id,
|
|
&comp_effecter_count,
|
|
field);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to decode SetStateEffecterStates request, rc = %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
return OPAL_INTERNAL_ERROR;
|
|
}
|
|
|
|
/* invoke the appropriate callback handler */
|
|
prlog(PR_DEBUG, "%s - effecter_id: %d, comp_effecter_count: %d\n",
|
|
__func__, effecter_id, comp_effecter_count);
|
|
|
|
for (i = 0; i < comp_effecter_count; i++) {
|
|
/* other set_request not supported */
|
|
if (field[i].set_request != PLDM_REQUEST_SET) {
|
|
prlog(PR_ERR, "Got invalid set request 0x%x in "
|
|
"SetStateEffecterStates request\n",
|
|
field[i].set_request);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
PLDM_PLATFORM_INVALID_STATE_VALUE);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
switch (field[i].effecter_state) {
|
|
case PLDM_SW_TERM_GRACEFUL_SHUTDOWN_REQUESTED:
|
|
case PLDM_STATE_SET_SYS_POWER_STATE_OFF_SOFT_GRACEFUL:
|
|
prlog(PR_NOTICE, "Soft shutdown requested\n");
|
|
cc_resp(rx, PLDM_PLATFORM,
|
|
PLDM_SET_STATE_EFFECTER_STATES,
|
|
PLDM_SUCCESS);
|
|
|
|
if (opal_booting() && platform.cec_power_down) {
|
|
prlog(PR_NOTICE, "Host not up, shutting down now\n");
|
|
platform.cec_power_down(CHASSIS_PWR_DOWN);
|
|
} else {
|
|
opal_queue_msg(OPAL_MSG_SHUTDOWN,
|
|
NULL, NULL,
|
|
cpu_to_be64(SOFT_OFF));
|
|
}
|
|
|
|
break;
|
|
|
|
case PLDM_SW_TERM_GRACEFUL_RESTART_REQUESTED:
|
|
prlog(PR_NOTICE, "Soft reboot requested\n");
|
|
cc_resp(rx, PLDM_PLATFORM,
|
|
PLDM_SET_STATE_EFFECTER_STATES,
|
|
PLDM_SUCCESS);
|
|
|
|
if (opal_booting() && platform.cec_reboot) {
|
|
prlog(PR_NOTICE, "Host not up, rebooting now\n");
|
|
platform.cec_reboot();
|
|
} else {
|
|
opal_queue_msg(OPAL_MSG_SHUTDOWN,
|
|
NULL, NULL,
|
|
cpu_to_be64(SOFT_REBOOT));
|
|
}
|
|
|
|
pldm_platform_initiate_shutdown();
|
|
break;
|
|
|
|
case PLDM_STATE_SET_BOOT_RESTART_CAUSE_WARM_RESET:
|
|
case PLDM_STATE_SET_BOOT_RESTART_CAUSE_HARD_RESET:
|
|
prlog(PR_NOTICE, "OCC reset requested\n");
|
|
cc_resp(rx, PLDM_PLATFORM,
|
|
PLDM_SET_STATE_EFFECTER_STATES,
|
|
PLDM_SUCCESS);
|
|
|
|
/* invoke the appropriate callback handler */
|
|
prd_occ_reset(DEFAULT_CHIP_ID); /* FIXME, others chip ? */
|
|
break;
|
|
|
|
default:
|
|
prlog(PR_ERR, "Got invalid effecter state 0x%x in "
|
|
"SetStateEffecterStates request\n",
|
|
field[i].effecter_state);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
PLDM_PLATFORM_INVALID_STATE_VALUE);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
}
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_platform_set_state_effecter_states = {
|
|
.name = "PLDM_SET_STATE_EFFECTER_STATES",
|
|
.pldm_cmd_id = PLDM_SET_STATE_EFFECTER_STATES,
|
|
.handler = platform_set_state_effecter_states_handler,
|
|
};
|
|
|
|
/*
|
|
* GetPDR (0x51)
|
|
* The GetPDR command is used to retrieve individual PDRs from a PDR
|
|
* Repository. The record is identified by the PDR recordHandle value
|
|
* that is passed in the request.
|
|
*/
|
|
static int platform_get_pdr_handle(const struct pldm_rx_data *rx)
|
|
{
|
|
uint32_t data_transfer_handle, pdr_data_size = 0;
|
|
uint32_t record_handle, next_record_handle;
|
|
uint16_t request_count, record_change_number;
|
|
uint8_t transfer_op_flag, *pdr_data = NULL;
|
|
size_t payload_len, data_size;
|
|
struct pldm_tx_data *tx;
|
|
int rc;
|
|
|
|
payload_len = rx->msg_len - sizeof(struct pldm_msg_hdr);
|
|
rc = decode_get_pdr_req(rx->msg,
|
|
payload_len,
|
|
&record_handle,
|
|
&data_transfer_handle,
|
|
&transfer_op_flag,
|
|
&request_count,
|
|
&record_change_number);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to decode GetPDR request, rc = %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
return OPAL_INTERNAL_ERROR;
|
|
}
|
|
|
|
if (data_transfer_handle != 0) {
|
|
/* We don't support multipart transfers */
|
|
prlog(PR_ERR, "Got invalid data transfer handle 0x%x in GetPDR request\n",
|
|
data_transfer_handle);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
PLDM_PLATFORM_INVALID_DATA_TRANSFER_HANDLE);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
if (transfer_op_flag != PLDM_GET_FIRSTPART) {
|
|
prlog(PR_ERR, "Got invalid transfer op flag 0x%x in GetPDR request\n",
|
|
transfer_op_flag);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
PLDM_PLATFORM_INVALID_TRANSFER_OPERATION_FLAG);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
if (record_change_number != 0) {
|
|
prlog(PR_ERR, "Got invalid record change number 0x%x in GetPDR request\n",
|
|
record_change_number);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
PLDM_PLATFORM_INVALID_RECORD_CHANGE_NUMBER);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* find PDR record by record handle */
|
|
prlog(PR_INFO, "BMC requesting PDR handle %d\n", record_handle);
|
|
|
|
rc = pldm_platform_pdr_find_record(record_handle,
|
|
&pdr_data,
|
|
&pdr_data_size,
|
|
&next_record_handle);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Got invalid record handle 0x%x in GetPDR request\n",
|
|
record_handle);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
PLDM_PLATFORM_INVALID_RECORD_HANDLE);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* create a PLDM response message for GetPDR */
|
|
data_size = sizeof(struct pldm_msg_hdr) +
|
|
sizeof(struct pldm_get_pdr_resp) +
|
|
pdr_data_size;
|
|
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size - 1;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
rc = encode_get_pdr_resp(rx->hdrinf.instance,
|
|
PLDM_SUCCESS,
|
|
next_record_handle,
|
|
0, /* No remaining data */
|
|
PLDM_START_AND_END,
|
|
pdr_data_size,
|
|
pdr_data,
|
|
0, /* CRC not used for START_AND_END */
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode GetPDR Error, rc: %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* send PLDM message over MCTP */
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send GetPDR response, rc = %d\n", rc);
|
|
free(tx);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_platform_get_pdr = {
|
|
.name = "PLDM_GET_PDR",
|
|
.pldm_cmd_id = PLDM_GET_PDR,
|
|
.handler = platform_get_pdr_handle,
|
|
};
|
|
|
|
/*
|
|
* PLDM Fru commands support
|
|
*/
|
|
static struct pldm_type pldm_fru_type = {
|
|
.name = "fru",
|
|
.pldm_type_id = PLDM_FRU,
|
|
};
|
|
|
|
/* currently we support version 1.0 of fru table */
|
|
#define SUPPORTED_FRU_VERSION_MAJOR 1
|
|
#define SUPPORTED_FRU_VERSION_MINOR 0
|
|
|
|
/* Used by the metadata request handler for the value of
|
|
* FRUTableMaximumSize
|
|
* 0 means SetFRURecordTable command is not supported (see DSP 0257
|
|
* v1.0.0 Table 9)
|
|
*/
|
|
#define FRU_TABLE_MAX_SIZE_UNSUPPORTED 0
|
|
|
|
/*
|
|
* GetFRURecordTableMetadata (0X01)
|
|
* The GetFRURecordTableMetadata command is used to get the FRU Record
|
|
* Table metadata information that includes the FRU Record major
|
|
* version, the FRU Record minor version, the size of the largest FRU
|
|
* Record data, total length of the FRU Record Table, total number of
|
|
* FRU Record Data structures, and the integrity checksum on the FRU
|
|
* Record Table data.
|
|
*/
|
|
static int fru_get_record_table_metadata_handler(const struct pldm_rx_data *rx)
|
|
{
|
|
size_t data_size = PLDM_MSG_SIZE(struct pldm_get_fru_record_table_metadata_resp);
|
|
uint16_t total_record_set_identifiers, total_table_records;
|
|
uint32_t fru_table_length;
|
|
struct pldm_tx_data *tx;
|
|
int rc;
|
|
|
|
/*
|
|
* GetFRURecordTableMetadata requests
|
|
* don't have any payload, so no need to decode them
|
|
*/
|
|
|
|
/* add specific fru record */
|
|
pldm_fru_set_local_table(&fru_table_length,
|
|
&total_record_set_identifiers,
|
|
&total_table_records);
|
|
|
|
/* create a PLDM response message for GetFRURecordTableMetadata */
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
rc = encode_get_fru_record_table_metadata_resp(
|
|
rx->hdrinf.instance,
|
|
PLDM_SUCCESS,
|
|
SUPPORTED_FRU_VERSION_MAJOR,
|
|
SUPPORTED_FRU_VERSION_MINOR,
|
|
FRU_TABLE_MAX_SIZE_UNSUPPORTED,
|
|
fru_table_length,
|
|
total_record_set_identifiers,
|
|
total_table_records,
|
|
0, // checksum, not calculated
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode GetFRURecordTableMetadata Error, rc: %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* send PLDM message over MCTP */
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send GetFRURecordTableMetadata response, rc = %d\n", rc);
|
|
free(tx);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_fru_get_record_table_metadata = {
|
|
.name = "PLDM_GET_FRU_RECORD_TABLE_METADATA",
|
|
.pldm_cmd_id = PLDM_GET_FRU_RECORD_TABLE_METADATA,
|
|
.handler = fru_get_record_table_metadata_handler,
|
|
};
|
|
|
|
/*
|
|
* GetFRURecordTable (0X02)
|
|
* The GetFRURecordTable command is used to get the FRU Record Table
|
|
* data. This command is defined to allow the FRU Record Table data to
|
|
* be transferred using a sequence of one or more command/response
|
|
* messages.
|
|
*/
|
|
static int fru_get_record_table_handler(const struct pldm_rx_data *rx)
|
|
{
|
|
struct pldm_get_fru_record_table_resp *resp;
|
|
void *fru_record_table_bytes;
|
|
uint32_t fru_record_table_size;
|
|
struct pldm_tx_data *tx;
|
|
struct pldm_msg *msg;
|
|
size_t data_size;
|
|
int rc;
|
|
|
|
/* The getFruRecordTable requests do have request data, but it's
|
|
* only related to multi-part transfers which we don't support
|
|
* and which the BMC will not send us.
|
|
*/
|
|
|
|
/* get local fru record table */
|
|
rc = pldm_fru_get_local_table(&fru_record_table_bytes, &fru_record_table_size);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to get Fru Record Table\n");
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
/* create a PLDM response message for GetFRURecordTable */
|
|
data_size = sizeof(struct pldm_msg_hdr) +
|
|
sizeof(struct pldm_get_fru_record_table_resp) +
|
|
fru_record_table_size;
|
|
|
|
tx = zalloc(sizeof(struct pldm_tx_data) + data_size);
|
|
if (!tx)
|
|
return OPAL_NO_MEM;
|
|
tx->data_size = data_size - 1;
|
|
tx->tag_owner = true;
|
|
tx->msg_tag = rx->msg_tag;
|
|
|
|
rc = encode_get_fru_record_table_resp(
|
|
rx->hdrinf.instance,
|
|
PLDM_SUCCESS,
|
|
0, // No next transfer handle
|
|
PLDM_START_AND_END,
|
|
(struct pldm_msg *)tx->data);
|
|
if (rc != PLDM_SUCCESS) {
|
|
prlog(PR_ERR, "Encode GetFruRecordTable Error, rc: %d\n", rc);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command, PLDM_ERROR);
|
|
free(tx);
|
|
return OPAL_PARAMETER;
|
|
}
|
|
|
|
msg = (struct pldm_msg *)tx->data;
|
|
resp = (struct pldm_get_fru_record_table_resp *)(msg->payload);
|
|
memcpy(resp->fru_record_table_data,
|
|
fru_record_table_bytes,
|
|
fru_record_table_size);
|
|
|
|
/* send PLDM message over MCTP */
|
|
rc = pldm_mctp_message_tx(tx);
|
|
if (rc) {
|
|
prlog(PR_ERR, "Failed to send GetFruRecordTable response, rc = %d\n", rc);
|
|
free(tx);
|
|
return OPAL_HARDWARE;
|
|
}
|
|
|
|
free(tx);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
|
|
static struct pldm_cmd pldm_fru_get_record_table = {
|
|
.name = "PLDM_GET_FRU_RECORD_TABLE",
|
|
.pldm_cmd_id = PLDM_GET_FRU_RECORD_TABLE,
|
|
.handler = fru_get_record_table_handler,
|
|
};
|
|
|
|
int pldm_responder_handle_request(struct pldm_rx_data *rx)
|
|
{
|
|
const struct pldm_type *type;
|
|
const struct pldm_cmd *cmd;
|
|
|
|
prlog(PR_INFO, "Receive PLDM request from BMC, type: 0x%x, command: 0x%x\n",
|
|
rx->hdrinf.pldm_type, rx->hdrinf.command);
|
|
|
|
type = find_type(rx->hdrinf.pldm_type);
|
|
if (!type) {
|
|
prlog(PR_ERR, "Type not supported, type: 0x%x\n",
|
|
rx->hdrinf.pldm_type);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
PLDM_ERROR_INVALID_PLDM_TYPE);
|
|
return OPAL_UNSUPPORTED;
|
|
}
|
|
|
|
cmd = find_cmd(type, rx->hdrinf.command);
|
|
if (!cmd) {
|
|
prlog(PR_ERR, "Command not supported, type: 0x%x, command: 0x%x\n",
|
|
rx->hdrinf.pldm_type, rx->hdrinf.command);
|
|
cc_resp(rx, rx->hdrinf.pldm_type,
|
|
rx->hdrinf.command,
|
|
PLDM_ERROR_UNSUPPORTED_PLDM_CMD);
|
|
return OPAL_UNSUPPORTED;
|
|
}
|
|
|
|
return cmd->handler(rx);
|
|
}
|
|
|
|
int pldm_responder_init(void)
|
|
{
|
|
/* Register mandatory commands we'll respond to - DSP0240 */
|
|
add_type(&pldm_base_type);
|
|
add_cmd(&pldm_base_type, &pldm_base_get_tid);
|
|
add_cmd(&pldm_base_type, &pldm_base_get_types);
|
|
add_cmd(&pldm_base_type, &pldm_base_get_commands);
|
|
add_cmd(&pldm_base_type, &pldm_base_get_version);
|
|
|
|
/* Register platform commands we'll respond to - DSP0248 */
|
|
add_type(&pldm_platform_type);
|
|
add_cmd(&pldm_platform_type, &pldm_platform_set_event_receiver);
|
|
add_cmd(&pldm_platform_type, &pldm_platform_event_message);
|
|
add_cmd(&pldm_platform_type, &pldm_platform_get_state_sensor_readings);
|
|
add_cmd(&pldm_platform_type, &pldm_platform_set_state_effecter_states);
|
|
add_cmd(&pldm_platform_type, &pldm_platform_get_pdr);
|
|
|
|
/* Register fru commands we'll respond to - DSP0257 */
|
|
add_type(&pldm_fru_type);
|
|
add_cmd(&pldm_fru_type, &pldm_fru_get_record_table_metadata);
|
|
add_cmd(&pldm_fru_type, &pldm_fru_get_record_table);
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|