blob: bd8e53a1a10686697a08f8e05a96bd20a18cf3e7 [file] [log] [blame]
/*
* Copyright (c) 2024, Arm Limited or its affiliates. All rights reserved.
*
* SPDX-License-Identifier: BSD-3-Clause
*
*/
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <debug.h>
#include <pcie.h>
#include <pcie_doe.h>
#include <tftf_lib.h>
static int pcie_doe_wait_ready(uint32_t bdf, uint32_t doe_cap_base)
{
uint32_t value;
for (unsigned int i = 0; i < PCI_DOE_POLL_LOOP; i++) {
value = pcie_read_cfg(bdf, doe_cap_base + DOE_STATUS_REG);
if ((value & DOE_STATUS_BUSY_BIT) != 0) {
ERROR("DOE Busy bit is set\n");
return -EBUSY;
}
if ((value & DOE_STATUS_ERROR_BIT) != 0) {
ERROR("DOE Error bit is set\n");
return -EIO;
}
if ((value & DOE_STATUS_READY_BIT) != 0) {
return 0;
}
waitms(PCI_DOE_POLL_TIME);
}
ERROR("DOE Timeout, status 0x%x\n", value);
return -ETIMEDOUT;
}
static const char * const doe_object_type[] = {
"DOE Discovery",
"CMA-SPDM",
"Secured CMA-SPDM",
/* PCI Express Base Specification Revision 6.1 */
"CMA/SPDM with Connection ID",
"Secured CMA/SPDM with Connection ID",
"Async Message"
};
void print_doe_disc(pcie_doe_disc_resp_t *data)
{
uint8_t type = data->data_object_type;
INFO("Vendor ID: 0x%x, ", data->vendor_id);
if (type >= ARRAY_SIZE(doe_object_type)) {
INFO("Unknown type: 0x%x\n", type);
} else {
INFO("%s\n", doe_object_type[type]);
}
}
static void print_doe_data(uint32_t idx, uint32_t data, bool last)
{
uint32_t j = idx + DOE_HEADER_LENGTH;
if (last) {
if ((j & 7) == 0) {
VERBOSE(" %08x\n", data);
} else {
VERBOSE(" %08x\n", data);
}
} else if ((j & 7) == 0) {
VERBOSE(" %08x", data);
} else if ((j & 7) == 7) {
VERBOSE(" %08x\n", data);
} else {
VERBOSE(" %08x", data);
}
}
/*
* @brief This API sends DOE request to PCI device.
* @param bdf - concatenated Bus(8-bits), device(8-bits) & function(8-bits)
* @param doe_cap_base - DOE capability base offset
* @param *req_addr - DOE request payload buffer
* @param req_len - DOE request payload length in bytes
*
* @return 0 on success, negative code on failure
*/
int pcie_doe_send_req(uint32_t header, uint32_t bdf, uint32_t doe_cap_base,
uint32_t *req_addr, uint32_t req_len)
{
uint32_t value, i, send_length, rem_length, doe_length;
value = pcie_read_cfg(bdf, doe_cap_base + DOE_STATUS_REG);
if ((value & DOE_STATUS_BUSY_BIT) != 0) {
ERROR("DOE Busy bit is set\n");
return -EBUSY;
}
if ((value & DOE_STATUS_ERROR_BIT) != 0) {
ERROR("DOE Error bit is set\n");
return -EIO;
}
send_length = req_len >> 2;
rem_length = req_len & 3;
/* Calculated adjusted data length in DW */
doe_length = (rem_length == 0) ? send_length : (send_length + 1);
VERBOSE(">%08x", header);
pcie_write_cfg(bdf, doe_cap_base + DOE_WRITE_DATA_MAILBOX_REG,
header);
VERBOSE(" %08x", doe_length + DOE_HEADER_LENGTH);
pcie_write_cfg(bdf, doe_cap_base + DOE_WRITE_DATA_MAILBOX_REG,
doe_length + DOE_HEADER_LENGTH);
/* Write data */
for (i = 0; i < send_length; i++) {
print_doe_data(i, req_addr[i], false);
pcie_write_cfg(bdf, doe_cap_base + DOE_WRITE_DATA_MAILBOX_REG,
req_addr[i]);
}
/* Check for remaining bytes */
if (rem_length != 0) {
value = 0;
(void)memcpy(&value, &req_addr[i], rem_length);
print_doe_data(i, value, true);
pcie_write_cfg(bdf, doe_cap_base + DOE_WRITE_DATA_MAILBOX_REG, value);
} else if (((i + DOE_HEADER_LENGTH) & 7) != 0) {
VERBOSE("\n");
}
/* Set Go bit */
pcie_write_cfg(bdf, doe_cap_base + DOE_CTRL_REG, DOE_CTRL_GO_BIT);
return 0;
}
/*
* @brief This API receives DOE response from PCI device.
* @param bdf - concatenated Bus(8-bits), device(8-bits) & function(8-bits)
* @param doe_cap_base - DOE capability base offset
* @param *resp_addr - DOE response payload buffer
* @param *resp_len - DOE response payload length in bytes
*
* @return 0 on success, negative code on failure
*/
int pcie_doe_recv_resp(uint32_t bdf, uint32_t doe_cap_base,
uint32_t *resp_addr, uint32_t *resp_len)
{
uint32_t i, value, length;
int ret;
/* Wait for Ready bit */
ret = pcie_doe_wait_ready(bdf, doe_cap_base);
if (ret != 0) {
return ret;
}
/*
* Reading DOE Header 1:
* Vendor ID and Data Object Type
*/
value = pcie_read_cfg(bdf, doe_cap_base + DOE_READ_DATA_MAILBOX_REG);
VERBOSE("<%08x", value);
/* Indicate a successful transfer of the current data object DW */
pcie_write_cfg(bdf, doe_cap_base + DOE_READ_DATA_MAILBOX_REG, 0);
/*
* Reading DOE Header 2:
* Length in DW
*/
value = pcie_read_cfg(bdf, doe_cap_base + DOE_READ_DATA_MAILBOX_REG);
VERBOSE(" %08x", value);
pcie_write_cfg(bdf, doe_cap_base + DOE_READ_DATA_MAILBOX_REG, 0);
/* Check value */
if ((value & PCI_DOE_RESERVED_MASK) != 0) {
VERBOSE("\n");
ERROR("DOE Data Object Header 2 error\n");
return -EIO;
}
/* Value of 00000h indicates 2^18 DW */
length = (value != 0) ? (value - DOE_HEADER_LENGTH) :
(PCI_DOE_MAX_LENGTH - DOE_HEADER_LENGTH);
/* Response payload length in bytes */
*resp_len = length << 2;
for (i = 0; i < length; i++) {
value = pcie_read_cfg(bdf, doe_cap_base + DOE_READ_DATA_MAILBOX_REG);
*resp_addr++ = value;
print_doe_data(i, value, false);
pcie_write_cfg(bdf, doe_cap_base + DOE_READ_DATA_MAILBOX_REG, 0);
}
if (((i + DOE_HEADER_LENGTH) & 7) != 0) {
VERBOSE("\n");
}
value = pcie_read_cfg(bdf, doe_cap_base + DOE_STATUS_REG);
if ((value & (DOE_STATUS_READY_BIT | DOE_STATUS_ERROR_BIT)) != 0) {
ERROR("DOE Receive error, status 0x%x\n", value);
return -EIO;
}
return 0;
}
int pcie_doe_communicate(uint32_t header, uint32_t bdf, uint32_t doe_cap_base,
void *req_buf, size_t req_sz, void *rsp_buf, size_t *rsp_sz)
{
int rc;
assert(header == DOE_HEADER_0 || header == DOE_HEADER_1 || header == DOE_HEADER_2);
rc = pcie_doe_send_req(header, bdf, doe_cap_base,
(uint32_t *)req_buf, (uint32_t)req_sz);
if (rc != 0) {
ERROR("PCIe DOE %s failed %d\n", "Request", rc);
return rc;
}
rc = pcie_doe_recv_resp(bdf, doe_cap_base, (uint32_t *)rsp_buf,
(uint32_t *)rsp_sz);
return rc;
}
int pcie_find_doe_device(uint32_t *bdf_ptr, uint32_t *cap_base_ptr)
{
pcie_device_bdf_table_t *bdf_table_ptr = pcie_get_bdf_table();
uint32_t num_bdf = bdf_table_ptr->num_entries;
INFO("PCI BDF table entries: %u\n", num_bdf);
/* If no entries in BDF table return error */
if (num_bdf == 0) {
ERROR("No BDFs entries found\n");
return -ENODEV;
}
INFO("PCI BDF table 0x%lx\n", (uintptr_t)bdf_table_ptr);
while (num_bdf-- != 0) {
uint32_t bdf = bdf_table_ptr->device[num_bdf].bdf;
uint32_t status, doe_cap_base;
/* Check for DOE capability */
status = pcie_find_capability(bdf, PCIE_ECAP, DOE_CAP_ID, &doe_cap_base);
if (status == PCIE_SUCCESS) {
INFO("PCIe DOE capability: bdf 0x%x cap_base 0x%x\n", bdf, doe_cap_base);
*bdf_ptr = bdf;
*cap_base_ptr = doe_cap_base;
return 0;
}
}
ERROR("No PCIe DOE capability found\n");
return -ENODEV;
}