mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
This was done entirely with mindless brute force, using
git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'
to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.
Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.
For the same reason the 'flex' versions will be done as a separate
conversion.
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
865 lines
22 KiB
C
865 lines
22 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
|
|
// Interface to PSP for CCP/SEV-TIO/SNP-VM
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/tsm.h>
|
|
#include <linux/psp.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/bitfield.h>
|
|
#include <linux/pci-doe.h>
|
|
#include <asm/sev-common.h>
|
|
#include <asm/sev.h>
|
|
#include <asm/page.h>
|
|
#include "sev-dev.h"
|
|
#include "sev-dev-tio.h"
|
|
|
|
#define to_tio_status(dev_data) \
|
|
(container_of((dev_data), struct tio_dsm, data)->sev->tio_status)
|
|
|
|
#define SLA_PAGE_TYPE_DATA 0
|
|
#define SLA_PAGE_TYPE_SCATTER 1
|
|
#define SLA_PAGE_SIZE_4K 0
|
|
#define SLA_PAGE_SIZE_2M 1
|
|
#define SLA_SZ(s) ((s).page_size == SLA_PAGE_SIZE_2M ? SZ_2M : SZ_4K)
|
|
#define SLA_SCATTER_LEN(s) (SLA_SZ(s) / sizeof(struct sla_addr_t))
|
|
#define SLA_EOL ((struct sla_addr_t) { .pfn = ((1UL << 40) - 1) })
|
|
#define SLA_NULL ((struct sla_addr_t) { 0 })
|
|
#define IS_SLA_NULL(s) ((s).sla == SLA_NULL.sla)
|
|
#define IS_SLA_EOL(s) ((s).sla == SLA_EOL.sla)
|
|
|
|
static phys_addr_t sla_to_pa(struct sla_addr_t sla)
|
|
{
|
|
u64 pfn = sla.pfn;
|
|
u64 pa = pfn << PAGE_SHIFT;
|
|
|
|
return pa;
|
|
}
|
|
|
|
static void *sla_to_va(struct sla_addr_t sla)
|
|
{
|
|
void *va = __va(__sme_clr(sla_to_pa(sla)));
|
|
|
|
return va;
|
|
}
|
|
|
|
#define sla_to_pfn(sla) (__pa(sla_to_va(sla)) >> PAGE_SHIFT)
|
|
#define sla_to_page(sla) virt_to_page(sla_to_va(sla))
|
|
|
|
static struct sla_addr_t make_sla(struct page *pg, bool stp)
|
|
{
|
|
u64 pa = __sme_set(page_to_phys(pg));
|
|
struct sla_addr_t ret = {
|
|
.pfn = pa >> PAGE_SHIFT,
|
|
.page_size = SLA_PAGE_SIZE_4K, /* Do not do SLA_PAGE_SIZE_2M ATM */
|
|
.page_type = stp ? SLA_PAGE_TYPE_SCATTER : SLA_PAGE_TYPE_DATA
|
|
};
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* the BUFFER Structure */
|
|
#define SLA_BUFFER_FLAG_ENCRYPTION BIT(0)
|
|
|
|
/*
|
|
* struct sla_buffer_hdr - Scatter list address buffer header
|
|
*
|
|
* @capacity_sz: Total capacity of the buffer in bytes
|
|
* @payload_sz: Size of buffer payload in bytes, must be multiple of 32B
|
|
* @flags: Buffer flags (SLA_BUFFER_FLAG_ENCRYPTION: buffer is encrypted)
|
|
* @iv: Initialization vector used for encryption
|
|
* @authtag: Authentication tag for encrypted buffer
|
|
*/
|
|
struct sla_buffer_hdr {
|
|
u32 capacity_sz;
|
|
u32 payload_sz; /* The size of BUFFER_PAYLOAD in bytes. Must be multiple of 32B */
|
|
u32 flags;
|
|
u8 reserved1[4];
|
|
u8 iv[16]; /* IV used for the encryption of this buffer */
|
|
u8 authtag[16]; /* Authentication tag for this buffer */
|
|
u8 reserved2[16];
|
|
} __packed;
|
|
|
|
enum spdm_data_type_t {
|
|
DOBJ_DATA_TYPE_SPDM = 0x1,
|
|
DOBJ_DATA_TYPE_SECURE_SPDM = 0x2,
|
|
};
|
|
|
|
struct spdm_dobj_hdr_req {
|
|
struct spdm_dobj_hdr hdr; /* hdr.id == SPDM_DOBJ_ID_REQ */
|
|
u8 data_type; /* spdm_data_type_t */
|
|
u8 reserved2[5];
|
|
} __packed;
|
|
|
|
struct spdm_dobj_hdr_resp {
|
|
struct spdm_dobj_hdr hdr; /* hdr.id == SPDM_DOBJ_ID_RESP */
|
|
u8 data_type; /* spdm_data_type_t */
|
|
u8 reserved2[5];
|
|
} __packed;
|
|
|
|
/* Defined in sev-dev-tio.h so sev-dev-tsm.c can read types of blobs */
|
|
struct spdm_dobj_hdr_cert;
|
|
struct spdm_dobj_hdr_meas;
|
|
struct spdm_dobj_hdr_report;
|
|
|
|
/* Used in all SPDM-aware TIO commands */
|
|
struct spdm_ctrl {
|
|
struct sla_addr_t req;
|
|
struct sla_addr_t resp;
|
|
struct sla_addr_t scratch;
|
|
struct sla_addr_t output;
|
|
} __packed;
|
|
|
|
static size_t sla_dobj_id_to_size(u8 id)
|
|
{
|
|
size_t n;
|
|
|
|
BUILD_BUG_ON(sizeof(struct spdm_dobj_hdr_resp) != 0x10);
|
|
switch (id) {
|
|
case SPDM_DOBJ_ID_REQ:
|
|
n = sizeof(struct spdm_dobj_hdr_req);
|
|
break;
|
|
case SPDM_DOBJ_ID_RESP:
|
|
n = sizeof(struct spdm_dobj_hdr_resp);
|
|
break;
|
|
default:
|
|
WARN_ON(1);
|
|
n = 0;
|
|
break;
|
|
}
|
|
|
|
return n;
|
|
}
|
|
|
|
#define SPDM_DOBJ_HDR_SIZE(hdr) sla_dobj_id_to_size((hdr)->id)
|
|
#define SPDM_DOBJ_DATA(hdr) ((u8 *)(hdr) + SPDM_DOBJ_HDR_SIZE(hdr))
|
|
#define SPDM_DOBJ_LEN(hdr) ((hdr)->length - SPDM_DOBJ_HDR_SIZE(hdr))
|
|
|
|
#define sla_to_dobj_resp_hdr(buf) ((struct spdm_dobj_hdr_resp *) \
|
|
sla_to_dobj_hdr_check((buf), SPDM_DOBJ_ID_RESP))
|
|
#define sla_to_dobj_req_hdr(buf) ((struct spdm_dobj_hdr_req *) \
|
|
sla_to_dobj_hdr_check((buf), SPDM_DOBJ_ID_REQ))
|
|
|
|
static struct spdm_dobj_hdr *sla_to_dobj_hdr(struct sla_buffer_hdr *buf)
|
|
{
|
|
if (!buf)
|
|
return NULL;
|
|
|
|
return (struct spdm_dobj_hdr *) &buf[1];
|
|
}
|
|
|
|
static struct spdm_dobj_hdr *sla_to_dobj_hdr_check(struct sla_buffer_hdr *buf, u32 check_dobjid)
|
|
{
|
|
struct spdm_dobj_hdr *hdr = sla_to_dobj_hdr(buf);
|
|
|
|
if (WARN_ON_ONCE(!hdr))
|
|
return NULL;
|
|
|
|
if (hdr->id != check_dobjid) {
|
|
pr_err("! ERROR: expected %d, found %d\n", check_dobjid, hdr->id);
|
|
return NULL;
|
|
}
|
|
|
|
return hdr;
|
|
}
|
|
|
|
static void *sla_to_data(struct sla_buffer_hdr *buf, u32 dobjid)
|
|
{
|
|
struct spdm_dobj_hdr *hdr = sla_to_dobj_hdr(buf);
|
|
|
|
if (WARN_ON_ONCE(dobjid != SPDM_DOBJ_ID_REQ && dobjid != SPDM_DOBJ_ID_RESP))
|
|
return NULL;
|
|
|
|
if (!hdr)
|
|
return NULL;
|
|
|
|
return (u8 *) hdr + sla_dobj_id_to_size(dobjid);
|
|
}
|
|
|
|
/*
|
|
* struct sev_data_tio_status - SEV_CMD_TIO_STATUS command
|
|
*
|
|
* @length: Length of this command buffer in bytes
|
|
* @status_paddr: System physical address of the TIO_STATUS structure
|
|
*/
|
|
struct sev_data_tio_status {
|
|
u32 length;
|
|
u8 reserved[4];
|
|
u64 status_paddr;
|
|
} __packed;
|
|
|
|
/* TIO_INIT */
|
|
struct sev_data_tio_init {
|
|
u32 length;
|
|
u8 reserved[12];
|
|
} __packed;
|
|
|
|
/*
|
|
* struct sev_data_tio_dev_create - TIO_DEV_CREATE command
|
|
*
|
|
* @length: Length in bytes of this command buffer
|
|
* @dev_ctx_sla: Scatter list address pointing to a buffer to be used as a device context buffer
|
|
* @device_id: PCIe Routing Identifier of the device to connect to
|
|
* @root_port_id: PCIe Routing Identifier of the root port of the device
|
|
* @segment_id: PCIe Segment Identifier of the device to connect to
|
|
*/
|
|
struct sev_data_tio_dev_create {
|
|
u32 length;
|
|
u8 reserved1[4];
|
|
struct sla_addr_t dev_ctx_sla;
|
|
u16 device_id;
|
|
u16 root_port_id;
|
|
u8 segment_id;
|
|
u8 reserved2[11];
|
|
} __packed;
|
|
|
|
/*
|
|
* struct sev_data_tio_dev_connect - TIO_DEV_CONNECT command
|
|
*
|
|
* @length: Length in bytes of this command buffer
|
|
* @spdm_ctrl: SPDM control structure defined in Section 5.1
|
|
* @dev_ctx_sla: Scatter list address of the device context buffer
|
|
* @tc_mask: Bitmask of the traffic classes to initialize for SEV-TIO usage.
|
|
* Setting the kth bit of the TC_MASK to 1 indicates that the traffic
|
|
* class k will be initialized
|
|
* @cert_slot: Slot number of the certificate requested for constructing the SPDM session
|
|
* @ide_stream_id: IDE stream IDs to be associated with this device.
|
|
* Valid only if corresponding bit in TC_MASK is set
|
|
*/
|
|
struct sev_data_tio_dev_connect {
|
|
u32 length;
|
|
u8 reserved1[4];
|
|
struct spdm_ctrl spdm_ctrl;
|
|
u8 reserved2[8];
|
|
struct sla_addr_t dev_ctx_sla;
|
|
u8 tc_mask;
|
|
u8 cert_slot;
|
|
u8 reserved3[6];
|
|
u8 ide_stream_id[8];
|
|
u8 reserved4[8];
|
|
} __packed;
|
|
|
|
/*
|
|
* struct sev_data_tio_dev_disconnect - TIO_DEV_DISCONNECT command
|
|
*
|
|
* @length: Length in bytes of this command buffer
|
|
* @flags: Command flags (TIO_DEV_DISCONNECT_FLAG_FORCE: force disconnect)
|
|
* @spdm_ctrl: SPDM control structure defined in Section 5.1
|
|
* @dev_ctx_sla: Scatter list address of the device context buffer
|
|
*/
|
|
#define TIO_DEV_DISCONNECT_FLAG_FORCE BIT(0)
|
|
|
|
struct sev_data_tio_dev_disconnect {
|
|
u32 length;
|
|
u32 flags;
|
|
struct spdm_ctrl spdm_ctrl;
|
|
struct sla_addr_t dev_ctx_sla;
|
|
} __packed;
|
|
|
|
/*
|
|
* struct sev_data_tio_dev_meas - TIO_DEV_MEASUREMENTS command
|
|
*
|
|
* @length: Length in bytes of this command buffer
|
|
* @flags: Command flags (TIO_DEV_MEAS_FLAG_RAW_BITSTREAM: request raw measurements)
|
|
* @spdm_ctrl: SPDM control structure defined in Section 5.1
|
|
* @dev_ctx_sla: Scatter list address of the device context buffer
|
|
* @meas_nonce: Nonce for measurement freshness verification
|
|
*/
|
|
#define TIO_DEV_MEAS_FLAG_RAW_BITSTREAM BIT(0)
|
|
|
|
struct sev_data_tio_dev_meas {
|
|
u32 length;
|
|
u32 flags;
|
|
struct spdm_ctrl spdm_ctrl;
|
|
struct sla_addr_t dev_ctx_sla;
|
|
u8 meas_nonce[32];
|
|
} __packed;
|
|
|
|
/*
|
|
* struct sev_data_tio_dev_certs - TIO_DEV_CERTIFICATES command
|
|
*
|
|
* @length: Length in bytes of this command buffer
|
|
* @spdm_ctrl: SPDM control structure defined in Section 5.1
|
|
* @dev_ctx_sla: Scatter list address of the device context buffer
|
|
*/
|
|
struct sev_data_tio_dev_certs {
|
|
u32 length;
|
|
u8 reserved[4];
|
|
struct spdm_ctrl spdm_ctrl;
|
|
struct sla_addr_t dev_ctx_sla;
|
|
} __packed;
|
|
|
|
/*
|
|
* struct sev_data_tio_dev_reclaim - TIO_DEV_RECLAIM command
|
|
*
|
|
* @length: Length in bytes of this command buffer
|
|
* @dev_ctx_sla: Scatter list address of the device context buffer
|
|
*
|
|
* This command reclaims resources associated with a device context.
|
|
*/
|
|
struct sev_data_tio_dev_reclaim {
|
|
u32 length;
|
|
u8 reserved[4];
|
|
struct sla_addr_t dev_ctx_sla;
|
|
} __packed;
|
|
|
|
static struct sla_buffer_hdr *sla_buffer_map(struct sla_addr_t sla)
|
|
{
|
|
struct sla_buffer_hdr *buf;
|
|
|
|
BUILD_BUG_ON(sizeof(struct sla_buffer_hdr) != 0x40);
|
|
if (IS_SLA_NULL(sla))
|
|
return NULL;
|
|
|
|
if (sla.page_type == SLA_PAGE_TYPE_SCATTER) {
|
|
struct sla_addr_t *scatter = sla_to_va(sla);
|
|
unsigned int i, npages = 0;
|
|
|
|
for (i = 0; i < SLA_SCATTER_LEN(sla); ++i) {
|
|
if (WARN_ON_ONCE(SLA_SZ(scatter[i]) > SZ_4K))
|
|
return NULL;
|
|
|
|
if (WARN_ON_ONCE(scatter[i].page_type == SLA_PAGE_TYPE_SCATTER))
|
|
return NULL;
|
|
|
|
if (IS_SLA_EOL(scatter[i])) {
|
|
npages = i;
|
|
break;
|
|
}
|
|
}
|
|
if (WARN_ON_ONCE(!npages))
|
|
return NULL;
|
|
|
|
struct page **pp = kmalloc_objs(pp[0], npages);
|
|
|
|
if (!pp)
|
|
return NULL;
|
|
|
|
for (i = 0; i < npages; ++i)
|
|
pp[i] = sla_to_page(scatter[i]);
|
|
|
|
buf = vm_map_ram(pp, npages, 0);
|
|
kfree(pp);
|
|
} else {
|
|
struct page *pg = sla_to_page(sla);
|
|
|
|
buf = vm_map_ram(&pg, 1, 0);
|
|
}
|
|
|
|
return buf;
|
|
}
|
|
|
|
static void sla_buffer_unmap(struct sla_addr_t sla, struct sla_buffer_hdr *buf)
|
|
{
|
|
if (!buf)
|
|
return;
|
|
|
|
if (sla.page_type == SLA_PAGE_TYPE_SCATTER) {
|
|
struct sla_addr_t *scatter = sla_to_va(sla);
|
|
unsigned int i, npages = 0;
|
|
|
|
for (i = 0; i < SLA_SCATTER_LEN(sla); ++i) {
|
|
if (IS_SLA_EOL(scatter[i])) {
|
|
npages = i;
|
|
break;
|
|
}
|
|
}
|
|
if (!npages)
|
|
return;
|
|
|
|
vm_unmap_ram(buf, npages);
|
|
} else {
|
|
vm_unmap_ram(buf, 1);
|
|
}
|
|
}
|
|
|
|
static void dobj_response_init(struct sla_buffer_hdr *buf)
|
|
{
|
|
struct spdm_dobj_hdr *dobj = sla_to_dobj_hdr(buf);
|
|
|
|
dobj->id = SPDM_DOBJ_ID_RESP;
|
|
dobj->version.major = 0x1;
|
|
dobj->version.minor = 0;
|
|
dobj->length = 0;
|
|
buf->payload_sz = sla_dobj_id_to_size(dobj->id) + dobj->length;
|
|
}
|
|
|
|
static void sla_free(struct sla_addr_t sla, size_t len, bool firmware_state)
|
|
{
|
|
unsigned int npages = PAGE_ALIGN(len) >> PAGE_SHIFT;
|
|
struct sla_addr_t *scatter = NULL;
|
|
int ret = 0, i;
|
|
|
|
if (IS_SLA_NULL(sla))
|
|
return;
|
|
|
|
if (firmware_state) {
|
|
if (sla.page_type == SLA_PAGE_TYPE_SCATTER) {
|
|
scatter = sla_to_va(sla);
|
|
|
|
for (i = 0; i < npages; ++i) {
|
|
if (IS_SLA_EOL(scatter[i]))
|
|
break;
|
|
|
|
ret = snp_reclaim_pages(sla_to_pa(scatter[i]), 1, false);
|
|
if (ret)
|
|
break;
|
|
}
|
|
} else {
|
|
ret = snp_reclaim_pages(sla_to_pa(sla), 1, false);
|
|
}
|
|
}
|
|
|
|
if (WARN_ON(ret))
|
|
return;
|
|
|
|
if (scatter) {
|
|
for (i = 0; i < npages; ++i) {
|
|
if (IS_SLA_EOL(scatter[i]))
|
|
break;
|
|
free_page((unsigned long)sla_to_va(scatter[i]));
|
|
}
|
|
}
|
|
|
|
free_page((unsigned long)sla_to_va(sla));
|
|
}
|
|
|
|
static struct sla_addr_t sla_alloc(size_t len, bool firmware_state)
|
|
{
|
|
unsigned long i, npages = PAGE_ALIGN(len) >> PAGE_SHIFT;
|
|
struct sla_addr_t *scatter = NULL;
|
|
struct sla_addr_t ret = SLA_NULL;
|
|
struct sla_buffer_hdr *buf;
|
|
struct page *pg;
|
|
|
|
if (npages == 0)
|
|
return ret;
|
|
|
|
if (WARN_ON_ONCE(npages > ((PAGE_SIZE / sizeof(struct sla_addr_t)) + 1)))
|
|
return ret;
|
|
|
|
BUILD_BUG_ON(PAGE_SIZE < SZ_4K);
|
|
|
|
if (npages > 1) {
|
|
pg = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
|
if (!pg)
|
|
return SLA_NULL;
|
|
|
|
ret = make_sla(pg, true);
|
|
scatter = page_to_virt(pg);
|
|
for (i = 0; i < npages; ++i) {
|
|
pg = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
|
if (!pg)
|
|
goto no_reclaim_exit;
|
|
|
|
scatter[i] = make_sla(pg, false);
|
|
}
|
|
scatter[i] = SLA_EOL;
|
|
} else {
|
|
pg = alloc_page(GFP_KERNEL | __GFP_ZERO);
|
|
if (!pg)
|
|
return SLA_NULL;
|
|
|
|
ret = make_sla(pg, false);
|
|
}
|
|
|
|
buf = sla_buffer_map(ret);
|
|
if (!buf)
|
|
goto no_reclaim_exit;
|
|
|
|
buf->capacity_sz = (npages << PAGE_SHIFT);
|
|
sla_buffer_unmap(ret, buf);
|
|
|
|
if (firmware_state) {
|
|
if (scatter) {
|
|
for (i = 0; i < npages; ++i) {
|
|
if (rmp_make_private(sla_to_pfn(scatter[i]), 0,
|
|
PG_LEVEL_4K, 0, true))
|
|
goto free_exit;
|
|
}
|
|
} else {
|
|
if (rmp_make_private(sla_to_pfn(ret), 0, PG_LEVEL_4K, 0, true))
|
|
goto no_reclaim_exit;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
|
|
no_reclaim_exit:
|
|
firmware_state = false;
|
|
free_exit:
|
|
sla_free(ret, len, firmware_state);
|
|
return SLA_NULL;
|
|
}
|
|
|
|
/* Expands a buffer, only firmware owned buffers allowed for now */
|
|
static int sla_expand(struct sla_addr_t *sla, size_t *len)
|
|
{
|
|
struct sla_buffer_hdr *oldbuf = sla_buffer_map(*sla), *newbuf;
|
|
struct sla_addr_t oldsla = *sla, newsla;
|
|
size_t oldlen = *len, newlen;
|
|
|
|
if (!oldbuf)
|
|
return -EFAULT;
|
|
|
|
newlen = oldbuf->capacity_sz;
|
|
if (oldbuf->capacity_sz == oldlen) {
|
|
/* This buffer does not require expansion, must be another buffer */
|
|
sla_buffer_unmap(oldsla, oldbuf);
|
|
return 1;
|
|
}
|
|
|
|
pr_notice("Expanding BUFFER from %ld to %ld bytes\n", oldlen, newlen);
|
|
|
|
newsla = sla_alloc(newlen, true);
|
|
if (IS_SLA_NULL(newsla))
|
|
return -ENOMEM;
|
|
|
|
newbuf = sla_buffer_map(newsla);
|
|
if (!newbuf) {
|
|
sla_free(newsla, newlen, true);
|
|
return -EFAULT;
|
|
}
|
|
|
|
memcpy(newbuf, oldbuf, oldlen);
|
|
|
|
sla_buffer_unmap(newsla, newbuf);
|
|
sla_free(oldsla, oldlen, true);
|
|
*sla = newsla;
|
|
*len = newlen;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sev_tio_do_cmd(int cmd, void *data, size_t data_len, int *psp_ret,
|
|
struct tsm_dsm_tio *dev_data)
|
|
{
|
|
int rc;
|
|
|
|
*psp_ret = 0;
|
|
rc = sev_do_cmd(cmd, data, psp_ret);
|
|
|
|
if (WARN_ON(!rc && *psp_ret == SEV_RET_SPDM_REQUEST))
|
|
return -EIO;
|
|
|
|
if (rc == 0 && *psp_ret == SEV_RET_EXPAND_BUFFER_LENGTH_REQUEST) {
|
|
int rc1, rc2;
|
|
|
|
rc1 = sla_expand(&dev_data->output, &dev_data->output_len);
|
|
if (rc1 < 0)
|
|
return rc1;
|
|
|
|
rc2 = sla_expand(&dev_data->scratch, &dev_data->scratch_len);
|
|
if (rc2 < 0)
|
|
return rc2;
|
|
|
|
if (!rc1 && !rc2)
|
|
/* Neither buffer requires expansion, this is wrong */
|
|
return -EFAULT;
|
|
|
|
*psp_ret = 0;
|
|
rc = sev_do_cmd(cmd, data, psp_ret);
|
|
}
|
|
|
|
if ((rc == 0 || rc == -EIO) && *psp_ret == SEV_RET_SPDM_REQUEST) {
|
|
struct spdm_dobj_hdr_resp *resp_hdr;
|
|
struct spdm_dobj_hdr_req *req_hdr;
|
|
struct sev_tio_status *tio_status = to_tio_status(dev_data);
|
|
size_t resp_len = tio_status->spdm_req_size_max -
|
|
(sla_dobj_id_to_size(SPDM_DOBJ_ID_RESP) + sizeof(struct sla_buffer_hdr));
|
|
|
|
if (!dev_data->cmd) {
|
|
if (WARN_ON_ONCE(!data_len || (data_len != *(u32 *) data)))
|
|
return -EINVAL;
|
|
if (WARN_ON(data_len > sizeof(dev_data->cmd_data)))
|
|
return -EFAULT;
|
|
memcpy(dev_data->cmd_data, data, data_len);
|
|
memset(&dev_data->cmd_data[data_len], 0xFF,
|
|
sizeof(dev_data->cmd_data) - data_len);
|
|
dev_data->cmd = cmd;
|
|
}
|
|
|
|
req_hdr = sla_to_dobj_req_hdr(dev_data->reqbuf);
|
|
resp_hdr = sla_to_dobj_resp_hdr(dev_data->respbuf);
|
|
switch (req_hdr->data_type) {
|
|
case DOBJ_DATA_TYPE_SPDM:
|
|
rc = PCI_DOE_FEATURE_CMA;
|
|
break;
|
|
case DOBJ_DATA_TYPE_SECURE_SPDM:
|
|
rc = PCI_DOE_FEATURE_SSESSION;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
resp_hdr->data_type = req_hdr->data_type;
|
|
dev_data->spdm.req_len = req_hdr->hdr.length -
|
|
sla_dobj_id_to_size(SPDM_DOBJ_ID_REQ);
|
|
dev_data->spdm.rsp_len = resp_len;
|
|
} else if (dev_data && dev_data->cmd) {
|
|
/* For either error or success just stop the bouncing */
|
|
memset(dev_data->cmd_data, 0, sizeof(dev_data->cmd_data));
|
|
dev_data->cmd = 0;
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
int sev_tio_continue(struct tsm_dsm_tio *dev_data)
|
|
{
|
|
struct spdm_dobj_hdr_resp *resp_hdr;
|
|
int ret;
|
|
|
|
if (!dev_data || !dev_data->cmd)
|
|
return -EINVAL;
|
|
|
|
resp_hdr = sla_to_dobj_resp_hdr(dev_data->respbuf);
|
|
resp_hdr->hdr.length = ALIGN(sla_dobj_id_to_size(SPDM_DOBJ_ID_RESP) +
|
|
dev_data->spdm.rsp_len, 32);
|
|
dev_data->respbuf->payload_sz = resp_hdr->hdr.length;
|
|
|
|
ret = sev_tio_do_cmd(dev_data->cmd, dev_data->cmd_data, 0,
|
|
&dev_data->psp_ret, dev_data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (dev_data->psp_ret != SEV_RET_SUCCESS)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void spdm_ctrl_init(struct spdm_ctrl *ctrl, struct tsm_dsm_tio *dev_data)
|
|
{
|
|
ctrl->req = dev_data->req;
|
|
ctrl->resp = dev_data->resp;
|
|
ctrl->scratch = dev_data->scratch;
|
|
ctrl->output = dev_data->output;
|
|
}
|
|
|
|
static void spdm_ctrl_free(struct tsm_dsm_tio *dev_data)
|
|
{
|
|
struct sev_tio_status *tio_status = to_tio_status(dev_data);
|
|
size_t len = tio_status->spdm_req_size_max -
|
|
(sla_dobj_id_to_size(SPDM_DOBJ_ID_RESP) +
|
|
sizeof(struct sla_buffer_hdr));
|
|
struct tsm_spdm *spdm = &dev_data->spdm;
|
|
|
|
sla_buffer_unmap(dev_data->resp, dev_data->respbuf);
|
|
sla_buffer_unmap(dev_data->req, dev_data->reqbuf);
|
|
spdm->rsp = NULL;
|
|
spdm->req = NULL;
|
|
sla_free(dev_data->req, len, true);
|
|
sla_free(dev_data->resp, len, false);
|
|
sla_free(dev_data->scratch, tio_status->spdm_scratch_size_max, true);
|
|
|
|
dev_data->req.sla = 0;
|
|
dev_data->resp.sla = 0;
|
|
dev_data->scratch.sla = 0;
|
|
dev_data->respbuf = NULL;
|
|
dev_data->reqbuf = NULL;
|
|
sla_free(dev_data->output, tio_status->spdm_out_size_max, true);
|
|
}
|
|
|
|
static int spdm_ctrl_alloc(struct tsm_dsm_tio *dev_data)
|
|
{
|
|
struct sev_tio_status *tio_status = to_tio_status(dev_data);
|
|
struct tsm_spdm *spdm = &dev_data->spdm;
|
|
int ret;
|
|
|
|
dev_data->req = sla_alloc(tio_status->spdm_req_size_max, true);
|
|
dev_data->resp = sla_alloc(tio_status->spdm_req_size_max, false);
|
|
dev_data->scratch_len = tio_status->spdm_scratch_size_max;
|
|
dev_data->scratch = sla_alloc(dev_data->scratch_len, true);
|
|
dev_data->output_len = tio_status->spdm_out_size_max;
|
|
dev_data->output = sla_alloc(dev_data->output_len, true);
|
|
|
|
if (IS_SLA_NULL(dev_data->req) || IS_SLA_NULL(dev_data->resp) ||
|
|
IS_SLA_NULL(dev_data->scratch) || IS_SLA_NULL(dev_data->dev_ctx)) {
|
|
ret = -ENOMEM;
|
|
goto free_spdm_exit;
|
|
}
|
|
|
|
dev_data->reqbuf = sla_buffer_map(dev_data->req);
|
|
dev_data->respbuf = sla_buffer_map(dev_data->resp);
|
|
if (!dev_data->reqbuf || !dev_data->respbuf) {
|
|
ret = -EFAULT;
|
|
goto free_spdm_exit;
|
|
}
|
|
|
|
spdm->req = sla_to_data(dev_data->reqbuf, SPDM_DOBJ_ID_REQ);
|
|
spdm->rsp = sla_to_data(dev_data->respbuf, SPDM_DOBJ_ID_RESP);
|
|
if (!spdm->req || !spdm->rsp) {
|
|
ret = -EFAULT;
|
|
goto free_spdm_exit;
|
|
}
|
|
|
|
dobj_response_init(dev_data->respbuf);
|
|
|
|
return 0;
|
|
|
|
free_spdm_exit:
|
|
spdm_ctrl_free(dev_data);
|
|
return ret;
|
|
}
|
|
|
|
int sev_tio_init_locked(void *tio_status_page)
|
|
{
|
|
struct sev_tio_status *tio_status = tio_status_page;
|
|
struct sev_data_tio_status data_status = {
|
|
.length = sizeof(data_status),
|
|
};
|
|
int ret, psp_ret;
|
|
|
|
data_status.status_paddr = __psp_pa(tio_status_page);
|
|
ret = __sev_do_cmd_locked(SEV_CMD_TIO_STATUS, &data_status, &psp_ret);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (tio_status->length < offsetofend(struct sev_tio_status, tdictx_size) ||
|
|
tio_status->reserved)
|
|
return -EFAULT;
|
|
|
|
if (!tio_status->tio_en && !tio_status->tio_init_done)
|
|
return -ENOENT;
|
|
|
|
if (tio_status->tio_init_done)
|
|
return -EBUSY;
|
|
|
|
struct sev_data_tio_init ti = { .length = sizeof(ti) };
|
|
|
|
ret = __sev_do_cmd_locked(SEV_CMD_TIO_INIT, &ti, &psp_ret);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = __sev_do_cmd_locked(SEV_CMD_TIO_STATUS, &data_status, &psp_ret);
|
|
if (ret)
|
|
return ret;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sev_tio_dev_create(struct tsm_dsm_tio *dev_data, u16 device_id,
|
|
u16 root_port_id, u8 segment_id)
|
|
{
|
|
struct sev_tio_status *tio_status = to_tio_status(dev_data);
|
|
struct sev_data_tio_dev_create create = {
|
|
.length = sizeof(create),
|
|
.device_id = device_id,
|
|
.root_port_id = root_port_id,
|
|
.segment_id = segment_id,
|
|
};
|
|
void *data_pg;
|
|
int ret;
|
|
|
|
dev_data->dev_ctx = sla_alloc(tio_status->devctx_size, true);
|
|
if (IS_SLA_NULL(dev_data->dev_ctx))
|
|
return -ENOMEM;
|
|
|
|
data_pg = snp_alloc_firmware_page(GFP_KERNEL_ACCOUNT);
|
|
if (!data_pg) {
|
|
ret = -ENOMEM;
|
|
goto free_ctx_exit;
|
|
}
|
|
|
|
create.dev_ctx_sla = dev_data->dev_ctx;
|
|
ret = sev_do_cmd(SEV_CMD_TIO_DEV_CREATE, &create, &dev_data->psp_ret);
|
|
if (ret)
|
|
goto free_data_pg_exit;
|
|
|
|
dev_data->data_pg = data_pg;
|
|
|
|
return 0;
|
|
|
|
free_data_pg_exit:
|
|
snp_free_firmware_page(data_pg);
|
|
free_ctx_exit:
|
|
sla_free(create.dev_ctx_sla, tio_status->devctx_size, true);
|
|
return ret;
|
|
}
|
|
|
|
int sev_tio_dev_reclaim(struct tsm_dsm_tio *dev_data)
|
|
{
|
|
struct sev_tio_status *tio_status = to_tio_status(dev_data);
|
|
struct sev_data_tio_dev_reclaim r = {
|
|
.length = sizeof(r),
|
|
.dev_ctx_sla = dev_data->dev_ctx,
|
|
};
|
|
int ret;
|
|
|
|
if (dev_data->data_pg) {
|
|
snp_free_firmware_page(dev_data->data_pg);
|
|
dev_data->data_pg = NULL;
|
|
}
|
|
|
|
if (IS_SLA_NULL(dev_data->dev_ctx))
|
|
return 0;
|
|
|
|
ret = sev_do_cmd(SEV_CMD_TIO_DEV_RECLAIM, &r, &dev_data->psp_ret);
|
|
|
|
sla_free(dev_data->dev_ctx, tio_status->devctx_size, true);
|
|
dev_data->dev_ctx = SLA_NULL;
|
|
|
|
spdm_ctrl_free(dev_data);
|
|
|
|
return ret;
|
|
}
|
|
|
|
int sev_tio_dev_connect(struct tsm_dsm_tio *dev_data, u8 tc_mask, u8 ids[8], u8 cert_slot)
|
|
{
|
|
struct sev_data_tio_dev_connect connect = {
|
|
.length = sizeof(connect),
|
|
.tc_mask = tc_mask,
|
|
.cert_slot = cert_slot,
|
|
.dev_ctx_sla = dev_data->dev_ctx,
|
|
.ide_stream_id = {
|
|
ids[0], ids[1], ids[2], ids[3],
|
|
ids[4], ids[5], ids[6], ids[7]
|
|
},
|
|
};
|
|
int ret;
|
|
|
|
if (WARN_ON(IS_SLA_NULL(dev_data->dev_ctx)))
|
|
return -EFAULT;
|
|
if (!(tc_mask & 1))
|
|
return -EINVAL;
|
|
|
|
ret = spdm_ctrl_alloc(dev_data);
|
|
if (ret)
|
|
return ret;
|
|
|
|
spdm_ctrl_init(&connect.spdm_ctrl, dev_data);
|
|
|
|
return sev_tio_do_cmd(SEV_CMD_TIO_DEV_CONNECT, &connect, sizeof(connect),
|
|
&dev_data->psp_ret, dev_data);
|
|
}
|
|
|
|
int sev_tio_dev_disconnect(struct tsm_dsm_tio *dev_data, bool force)
|
|
{
|
|
struct sev_data_tio_dev_disconnect dc = {
|
|
.length = sizeof(dc),
|
|
.dev_ctx_sla = dev_data->dev_ctx,
|
|
.flags = force ? TIO_DEV_DISCONNECT_FLAG_FORCE : 0,
|
|
};
|
|
|
|
if (WARN_ON_ONCE(IS_SLA_NULL(dev_data->dev_ctx)))
|
|
return -EFAULT;
|
|
|
|
spdm_ctrl_init(&dc.spdm_ctrl, dev_data);
|
|
|
|
return sev_tio_do_cmd(SEV_CMD_TIO_DEV_DISCONNECT, &dc, sizeof(dc),
|
|
&dev_data->psp_ret, dev_data);
|
|
}
|
|
|
|
int sev_tio_cmd_buffer_len(int cmd)
|
|
{
|
|
switch (cmd) {
|
|
case SEV_CMD_TIO_STATUS: return sizeof(struct sev_data_tio_status);
|
|
case SEV_CMD_TIO_INIT: return sizeof(struct sev_data_tio_init);
|
|
case SEV_CMD_TIO_DEV_CREATE: return sizeof(struct sev_data_tio_dev_create);
|
|
case SEV_CMD_TIO_DEV_RECLAIM: return sizeof(struct sev_data_tio_dev_reclaim);
|
|
case SEV_CMD_TIO_DEV_CONNECT: return sizeof(struct sev_data_tio_dev_connect);
|
|
case SEV_CMD_TIO_DEV_DISCONNECT: return sizeof(struct sev_data_tio_dev_disconnect);
|
|
default: return 0;
|
|
}
|
|
}
|