blob: 33f9b17f14a8a5bb4148725e4b7e3ade717542ec [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 <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,
};
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);
return OPAL_SUCCESS;
}