blob: 9183b097cc1d1d8db996bc17c776e531dbca1991 [file] [log] [blame]
// 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 <stdio.h>
#include <string.h>
#include <debug_descriptor.h>
#include <libpldm/platform.h>
#include <libpldm/platform_oem_ibm.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,
};
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);
return OPAL_SUCCESS;
}