blob: 9830cda993672674dd7fd8cba7e63a93c9f99853 [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 <cpu.h>
#include <stdio.h>
#include <string.h>
#include <timebase.h>
#include <ast.h>
#include <libpldm/utils.h>
#include "pldm.h"
#define TIMEOUT_MS 8000
struct pldm_request {
struct list_node link;
/* originating request params */
struct pldm_header_info hdrinf;
/* messages requested */
struct pldm_tx_data *tx;
/* timeout handling */
struct timer timeout;
uint64_t timeout_ms;
uint64_t start_time;
/* completion callback */
void (*complete)(struct pldm_rx_data *rx, void *data);
void *complete_data;
};
struct pldm_response {
void **msg;
size_t *msg_size;
bool done;
int rc;
};
/* pldm requests queue */
static struct lock pldm_requests_lock = LOCK_UNLOCKED;
static LIST_HEAD(list_pldm_requests);
static struct pldm_request *active_request;
static bool matches_request(const struct pldm_rx_data *rx,
const struct pldm_request *req)
{
if (req->hdrinf.instance != rx->hdrinf.instance)
return false;
if (req->hdrinf.pldm_type != rx->hdrinf.pldm_type)
return false;
if (req->hdrinf.command != rx->hdrinf.command)
return false;
return true;
}
static void send_and_wait_complete(struct pldm_rx_data *rx, void *data)
{
struct pldm_response *resp = (struct pldm_response *)data;
int len;
if (rx != NULL) {
len = rx->msg_len;
*resp->msg_size = len;
*resp->msg = zalloc(len);
memcpy(*resp->msg, rx->msg, len);
resp->rc = OPAL_SUCCESS;
} else {
*resp->msg_size = 0;
*resp->msg = NULL;
resp->rc = OPAL_TIMEOUT;
}
resp->done = true;
}
static void handle_response(struct pldm_rx_data *rx)
{
uint64_t now;
if (active_request == NULL) {
prlog(PR_ERR, "%s: No active request\n", __func__);
return;
}
/* unactivate the timer */
if (rx != NULL)
cancel_timer(&active_request->timeout);
if (active_request->complete)
active_request->complete(rx, active_request->complete_data);
now = mftb();
prlog(PR_TRACE, "%s: Finished after %ldms, t:%d c:%d i:%d\n",
__func__,
tb_to_msecs(now - active_request->start_time),
active_request->hdrinf.pldm_type,
active_request->hdrinf.command,
active_request->hdrinf.instance);
free(active_request->tx);
free(active_request);
active_request = NULL;
}
/*
* Timeout :(
*/
static void expiry(struct timer *t __unused, void *data, uint64_t now __unused)
{
struct pldm_request *req = (struct pldm_request *)data;
if (active_request == NULL) {
prlog(PR_ERR, "request timedout! (active request NULL)\n");
return;
}
prlog(PR_ERR, "PLDM: request timedout! (active request: t:0x%x c:0x%x i:%d)\n",
active_request->hdrinf.pldm_type,
active_request->hdrinf.command,
active_request->hdrinf.instance);
prlog(PR_ERR, "PLDM: Original request t:0x%x c:0x%x i:%d -----\n",
req->hdrinf.pldm_type,
req->hdrinf.command, req->hdrinf.instance);
/* no data received. Finish the procedure */
handle_response(NULL);
}
/*
* Handle PLDM message received from the PLDM terminus over MCTP
*/
int pldm_requester_handle_response(struct pldm_rx_data *rx)
{
/* check the message received */
if (active_request == NULL) {
prlog(PR_ERR, "%s: No active request. "
"Response received t:%d c:%d i:%d\n",
__func__,
rx->hdrinf.pldm_type,
rx->hdrinf.command,
rx->hdrinf.instance);
return OPAL_WRONG_STATE;
}
if (!matches_request(rx, active_request)) {
prlog(PR_ERR, "%s: Unexpected response! t:%d c:%d i:%d want %d,%d,%d\n",
__func__,
rx->hdrinf.pldm_type,
rx->hdrinf.command,
rx->hdrinf.instance,
active_request->hdrinf.pldm_type,
active_request->hdrinf.command,
active_request->hdrinf.instance);
return OPAL_WRONG_STATE;
}
/* The expected message seems correct */
handle_response(rx);
return OPAL_SUCCESS;
}
/*
* Send the PLDM request
*/
static void requests_poller(void *data __unused)
{
int rc = OPAL_SUCCESS;
lock(&pldm_requests_lock);
/* wait for the end of the processing of the current request */
if (active_request) {
unlock(&pldm_requests_lock);
return;
}
/* no new request to handle */
if (list_empty(&list_pldm_requests)) {
unlock(&pldm_requests_lock);
return;
}
/* remove the first entry in a list */
active_request = list_pop(&list_pldm_requests,
struct pldm_request,
link);
unlock(&pldm_requests_lock);
/* Start timer to control a timeout from the PLDM terminus */
init_timer(&active_request->timeout, expiry, active_request);
schedule_timer(&active_request->timeout,
msecs_to_tb(active_request->timeout_ms));
active_request->start_time = mftb();
/* Send PLDM message over MCTP */
prlog(PR_TRACE, "%s: Sending request to BMC t:%d c:%d i:%d -----\n",
__func__,
active_request->hdrinf.pldm_type,
active_request->hdrinf.command,
active_request->hdrinf.instance);
rc = pldm_mctp_message_tx(active_request->tx);
if (rc)
prlog(PR_ERR, "%s: Error %d while sending request\n",
__func__, rc);
}
/*
* Add PLDM request in the queue
*/
static int queue_request(struct pldm_tx_data *tx,
uint64_t timeout_ms,
void (*complete)(struct pldm_rx_data *rx, void *data),
void *complete_data)
{
struct pldm_request *pending;
struct pldm_msg *pldm_msg;
size_t tx_size;
tx_size = sizeof(struct pldm_tx_data) + tx->data_size;
pending = zalloc(sizeof(struct pldm_request));
if (!pending) {
prlog(PR_ERR, "%s: failed to allocate request\n", __func__);
return OPAL_NO_MEM;
}
pending->timeout_ms = timeout_ms;
pending->complete = complete;
pending->complete_data = complete_data;
pending->tx = zalloc(tx_size);
if (!pending->tx) {
free(pending);
prlog(PR_ERR, "%s: failed to allocate pldm packet (size: 0x%lx)\n",
__func__, tx_size);
return OPAL_NO_MEM;
}
memcpy(pending->tx, tx, tx_size);
pldm_msg = (struct pldm_msg *)tx->data;
if (unpack_pldm_header(&pldm_msg->hdr, &pending->hdrinf)) {
free(pending->tx);
free(pending);
prlog(PR_ERR, "%s: error parsing pldm header\n", __func__);
return OPAL_PARAMETER;
}
/* add an entry at the end of a linked list */
prlog(PR_TRACE, "%s: Add request t:%d c:%d i:%d -----\n",
__func__,
pending->hdrinf.pldm_type,
pending->hdrinf.command,
pending->hdrinf.instance);
lock(&pldm_requests_lock);
list_add_tail(&list_pldm_requests, &pending->link);
unlock(&pldm_requests_lock);
return OPAL_SUCCESS;
}
/*
* Queue a PLDM request and don't wait.
* When a response is received, call the associated callback.
*/
int pldm_requester_queue(struct pldm_tx_data *tx,
void (*complete)(struct pldm_rx_data *rx, void *data),
void *complete_data)
{
int rc = OPAL_SUCCESS;
/* Queue PLDM request */
rc = queue_request(tx, TIMEOUT_MS, complete, complete_data);
if (rc) {
prlog(PR_ERR, "%s: error %d while queuing request\n",
__func__, rc);
return rc;
}
return rc;
}
/*
* Queue a PLDM request and spin until we get a response.
*/
int pldm_requester_queue_and_wait(struct pldm_tx_data *tx,
void **msg, size_t *msg_size)
{
struct pldm_response *resp;
int rc = OPAL_SUCCESS;
resp = zalloc(sizeof(struct pldm_response));
if (!resp) {
prlog(PR_ERR, "%s: failed to allocate response\n", __func__);
return OPAL_NO_MEM;
}
resp->msg = msg;
resp->msg_size = msg_size;
rc = pldm_requester_queue(tx, send_and_wait_complete, resp);
if (rc)
goto out;
/* wait for a response from the BMC */
for (;;) {
if (resp->done)
break;
time_wait_ms(5);
}
rc = resp->rc;
out:
free(resp);
return rc;
}
int pldm_requester_init(void)
{
/* requests poller */
opal_add_poller(requests_poller, NULL);
return OPAL_SUCCESS;
}