The driver to communicate with Secure Encrypted Virtualization (SEV) firmware running within the AMD secure processor providing a secure key management interface for SEV guests. Signed-off-by: Tom Lendacky <thomas.lendacky@xxxxxxx> Signed-off-by: Brijesh Singh <brijesh.singh@xxxxxxx> --- drivers/crypto/Kconfig | 11 + drivers/crypto/Makefile | 1 drivers/crypto/psp/Kconfig | 8 drivers/crypto/psp/Makefile | 3 drivers/crypto/psp/psp-dev.c | 220 +++++++++++ drivers/crypto/psp/psp-dev.h | 95 +++++ drivers/crypto/psp/psp-ops.c | 454 +++++++++++++++++++++++ drivers/crypto/psp/psp-pci.c | 376 +++++++++++++++++++ include/linux/ccp-psp.h | 833 ++++++++++++++++++++++++++++++++++++++++++ include/uapi/linux/Kbuild | 1 include/uapi/linux/ccp-psp.h | 182 +++++++++ 11 files changed, 2184 insertions(+) create mode 100644 drivers/crypto/psp/Kconfig create mode 100644 drivers/crypto/psp/Makefile create mode 100644 drivers/crypto/psp/psp-dev.c create mode 100644 drivers/crypto/psp/psp-dev.h create mode 100644 drivers/crypto/psp/psp-ops.c create mode 100644 drivers/crypto/psp/psp-pci.c create mode 100644 include/linux/ccp-psp.h create mode 100644 include/uapi/linux/ccp-psp.h diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 1af94e2..3bdbc51 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -464,6 +464,17 @@ if CRYPTO_DEV_CCP source "drivers/crypto/ccp/Kconfig" endif +config CRYPTO_DEV_PSP + bool "Support for AMD Platform Security Processor" + depends on X86 && PCI + help + The AMD Platform Security Processor provides hardware key- + management services for VMGuard encrypted memory. + +if CRYPTO_DEV_PSP + source "drivers/crypto/psp/Kconfig" +endif + config CRYPTO_DEV_MXS_DCP tristate "Support for Freescale MXS DCP" depends on (ARCH_MXS || ARCH_MXC) diff --git a/drivers/crypto/Makefile b/drivers/crypto/Makefile index 3c6432d..1ea1e08 100644 --- a/drivers/crypto/Makefile +++ b/drivers/crypto/Makefile @@ -3,6 +3,7 @@ obj-$(CONFIG_CRYPTO_DEV_ATMEL_SHA) += atmel-sha.o obj-$(CONFIG_CRYPTO_DEV_ATMEL_TDES) += atmel-tdes.o obj-$(CONFIG_CRYPTO_DEV_BFIN_CRC) += bfin_crc.o obj-$(CONFIG_CRYPTO_DEV_CCP) += ccp/ +obj-$(CONFIG_CRYPTO_DEV_PSP) += psp/ obj-$(CONFIG_CRYPTO_DEV_FSL_CAAM) += caam/ obj-$(CONFIG_CRYPTO_DEV_GEODE) += geode-aes.o obj-$(CONFIG_CRYPTO_DEV_HIFN_795X) += hifn_795x.o diff --git a/drivers/crypto/psp/Kconfig b/drivers/crypto/psp/Kconfig new file mode 100644 index 0000000..acd9b87 --- /dev/null +++ b/drivers/crypto/psp/Kconfig @@ -0,0 +1,8 @@ +config CRYPTO_DEV_PSP_DD + tristate "PSP Key Management device driver" + depends on CRYPTO_DEV_PSP + default m + help + Provides the interface to use the AMD PSP key management APIs + for use with the AMD Secure Enhanced Virtualization. If you + choose 'M' here, this module will be called psp. diff --git a/drivers/crypto/psp/Makefile b/drivers/crypto/psp/Makefile new file mode 100644 index 0000000..1b7d00c --- /dev/null +++ b/drivers/crypto/psp/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_CRYPTO_DEV_PSP_DD) += psp.o +psp-objs := psp-dev.o psp-ops.o +psp-$(CONFIG_PCI) += psp-pci.o diff --git a/drivers/crypto/psp/psp-dev.c b/drivers/crypto/psp/psp-dev.c new file mode 100644 index 0000000..65d5c7e --- /dev/null +++ b/drivers/crypto/psp/psp-dev.c @@ -0,0 +1,220 @@ +/* + * AMD Cryptographic Coprocessor (CCP) driver + * + * Copyright (C) 2016 Advanced Micro Devices, Inc. + * + * Author: Tom Lendacky <thomas.lendacky@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/interrupt.h> +#include <linux/dma-mapping.h> +#include <linux/string.h> +#include <linux/wait.h> + +#include "psp-dev.h" + +MODULE_AUTHOR("Advanced Micro Devices, Inc."); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1.0"); +MODULE_DESCRIPTION("AMD VMGuard key-management driver prototype"); + +static struct psp_device *psp_master; + +static LIST_HEAD(psp_devs); +static DEFINE_SPINLOCK(psp_devs_lock); + +static atomic_t psp_id; + +static void psp_add_device(struct psp_device *psp) +{ + unsigned long flags; + + spin_lock_irqsave(&psp_devs_lock, flags); + + list_add_tail(&psp->entry, &psp_devs); + psp_master = psp->get_master(&psp_devs); + + spin_unlock_irqrestore(&psp_devs_lock, flags); +} + +static void psp_del_device(struct psp_device *psp) +{ + unsigned long flags; + + spin_lock_irqsave(&psp_devs_lock, flags); + + list_del(&psp->entry); + if (psp == psp_master) + psp_master = NULL; + + spin_unlock_irqrestore(&psp_devs_lock, flags); +} + +static void psp_check_support(struct psp_device *psp) +{ + if (ioread32(psp->io_regs + PSP_CMDRESP)) + psp->sev_enabled = 1; +} + +/** + * psp_get_master_device - returns a pointer to the PSP master device structure + * + * Returns NULL if a PSP master device is not present, PSP device structure + * otherwise. + */ +struct psp_device *psp_get_master_device(void) +{ + return psp_master; +} +EXPORT_SYMBOL_GPL(psp_get_master_device); + +/** + * psp_get_device - returns a pointer to the PSP device structure + * + * Returns NULL if a PSP device is not present, PSP device structure otherwise. + */ +struct psp_device *psp_get_device(void) +{ + struct psp_device *psp = NULL; + unsigned long flags; + + spin_lock_irqsave(&psp_devs_lock, flags); + + if (list_empty(&psp_devs)) + goto unlock; + + psp = list_first_entry(&psp_devs, struct psp_device, entry); + +unlock: + spin_unlock_irqrestore(&psp_devs_lock, flags); + + return psp; +} +EXPORT_SYMBOL_GPL(psp_get_device); + +/** + * psp_alloc_struct - allocate and initialize the psp_device struct + * + * @dev: device struct of the PSP + */ +struct psp_device *psp_alloc_struct(struct device *dev) +{ + struct psp_device *psp; + + psp = devm_kzalloc(dev, sizeof(*psp), GFP_KERNEL); + if (psp == NULL) { + dev_err(dev, "unable to allocate device struct\n"); + return NULL; + } + psp->dev = dev; + + psp->id = atomic_inc_return(&psp_id); + snprintf(psp->name, sizeof(psp->name), "psp%u", psp->id); + + init_waitqueue_head(&psp->int_queue); + + return psp; +} + +/** + * psp_init - initialize the PSP device + * + * @psp: psp_device struct + */ +int psp_init(struct psp_device *psp) +{ + int ret; + + psp_check_support(psp); + + /* Disable and clear interrupts until ready */ + iowrite32(0, psp->io_regs + PSP_P2CMSG_INTEN); + iowrite32(0xffffffff, psp->io_regs + PSP_P2CMSG_INTSTS); + + /* Request an irq */ + ret = psp->get_irq(psp); + if (ret) { + dev_err(psp->dev, "unable to allocate IRQ\n"); + return ret; + } + + /* Make the device struct available */ + psp_add_device(psp); + + /* Enable interrupts */ + iowrite32(1 << PSP_CMD_COMPLETE_REG, psp->io_regs + PSP_P2CMSG_INTEN); + + ret = psp_ops_init(psp); + if (ret) + dev_err(psp->dev, "psp_ops_init returned %d\n", ret); + + return 0; +} + +/** + * psp_destroy - tear down the PSP device + * + * @psp: psp_device struct + */ +void psp_destroy(struct psp_device *psp) +{ + psp_ops_exit(psp); + + /* Remove general access to the device struct */ + psp_del_device(psp); + + psp->free_irq(psp); +} + +/** + * psp_irq_handler - handle interrupts generated by the PSP device + * + * @irq: the irq associated with the interrupt + * @data: the data value supplied when the irq was created + */ +irqreturn_t psp_irq_handler(int irq, void *data) +{ + struct device *dev = data; + struct psp_device *psp = dev_get_drvdata(dev); + unsigned int status; + + status = ioread32(psp->io_regs + PSP_P2CMSG_INTSTS); + if (status & (1 << PSP_CMD_COMPLETE_REG)) { + int reg; + + reg = ioread32(psp->io_regs + PSP_CMDRESP); + if (reg & PSP_CMDRESP_RESP) { + psp->int_rcvd = 1; + wake_up_interruptible(&psp->int_queue); + } + } + + iowrite32(status, psp->io_regs + PSP_P2CMSG_INTSTS); + + return IRQ_HANDLED; +} + +static int __init psp_mod_init(void) +{ + int ret; + + ret = psp_pci_init(); + if (ret) + return ret; + + return 0; +} +module_init(psp_mod_init); + +static void __exit psp_mod_exit(void) +{ + psp_pci_exit(); +} +module_exit(psp_mod_exit); diff --git a/drivers/crypto/psp/psp-dev.h b/drivers/crypto/psp/psp-dev.h new file mode 100644 index 0000000..bb75ca2 --- /dev/null +++ b/drivers/crypto/psp/psp-dev.h @@ -0,0 +1,95 @@ + +#ifndef __PSP_DEV_H__ +#define __PSP_DEV_H__ + +#include <linux/device.h> +#include <linux/pci.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/list.h> +#include <linux/wait.h> +#include <linux/dmapool.h> +#include <linux/hw_random.h> +#include <linux/interrupt.h> +#include <linux/miscdevice.h> + +#define PSP_P2CMSG_INTEN 0x0110 +#define PSP_P2CMSG_INTSTS 0x0114 + +#define PSP_C2PMSG_ATTR_0 0x0118 +#define PSP_C2PMSG_ATTR_1 0x011c +#define PSP_C2PMSG_ATTR_2 0x0120 +#define PSP_C2PMSG_ATTR_3 0x0124 +#define PSP_P2CMSG_ATTR_0 0x0128 + +#define PSP_C2PMSG(_num) ((_num) << 2) +#define PSP_CMDRESP PSP_C2PMSG(32) +#define PSP_CMDBUFF_ADDR_LO PSP_C2PMSG(56) +#define PSP_CMDBUFF_ADDR_HI PSP_C2PMSG(57) + +#define PSP_P2CMSG(_num) (_num << 2) +#define PSP_CMD_COMPLETE_REG 1 +#define PSP_CMD_COMPLETE PSP_P2CMSG(PSP_CMD_COMPLETE_REG) + +#define PSP_CMDRESP_CMD_SHIFT 16 +#define PSP_CMDRESP_IOC BIT(0) +#define PSP_CMDRESP_RESP BIT(31) +#define PSP_CMDRESP_ERR_MASK 0xffff + +#define PSP_DRIVER_NAME "psp" + +struct psp_device { + struct list_head entry; + + struct device *dev; + + unsigned int id; + char name[32]; + + struct dentry *debugfs; + struct miscdevice misc; + + unsigned int sev_enabled; + + /* + * Bus-specific device information + */ + void *dev_specific; + int (*get_irq)(struct psp_device *); + void (*free_irq)(struct psp_device *); + unsigned int irq; + struct psp_device *(*get_master)(struct list_head *list); + + /* + * I/O area used for device communication. Writing to the + * mailbox registers generates an interrupt on the PSP. + */ + void __iomem *io_map; + void __iomem *io_regs; + + /* Interrupt wait queue */ + wait_queue_head_t int_queue; + unsigned int int_rcvd; +}; + +struct psp_device *psp_get_master_device(void); +struct psp_device *psp_get_device(void); + +#ifdef CONFIG_PCI +int psp_pci_init(void); +void psp_pci_exit(void); +#else +static inline int psp_pci_init(void) { return 0; } +static inline void psp_pci_exit(void) { } +#endif + +struct psp_device *psp_alloc_struct(struct device *dev); +int psp_init(struct psp_device *psp); +void psp_destroy(struct psp_device *psp); + +int psp_ops_init(struct psp_device *psp); +void psp_ops_exit(struct psp_device *psp); + +irqreturn_t psp_irq_handler(int irq, void *data); + +#endif /* PSP_DEV_H */ diff --git a/drivers/crypto/psp/psp-ops.c b/drivers/crypto/psp/psp-ops.c new file mode 100644 index 0000000..81e8dc8 --- /dev/null +++ b/drivers/crypto/psp/psp-ops.c @@ -0,0 +1,454 @@ + +#include <linux/module.h> +#include <linux/fs.h> +#include <linux/miscdevice.h> +#include <linux/delay.h> +#include <linux/jiffies.h> +#include <linux/wait.h> +#include <linux/mutex.h> +#include <linux/ccp-psp.h> + +#include "psp-dev.h" + +static unsigned int psp_poll = 0; +module_param(psp_poll, uint, 0444); +MODULE_PARM_DESC(psp_poll, "Poll for command completion - any non-zero value"); + +#define PSP_DEFAULT_TIMEOUT 2 + +DEFINE_MUTEX(psp_cmd_mutex); + +static int psp_wait_cmd_poll(struct psp_device *psp, unsigned int timeout, + unsigned int *reg) +{ + int wait = timeout * 10; /* 100ms sleep => timeout * 10 */ + + while (--wait) { + msleep(100); + + *reg = ioread32(psp->io_regs + PSP_CMDRESP); + if (*reg & PSP_CMDRESP_RESP) + break; + } + + if (!wait) { + dev_err(psp->dev, "psp command timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int psp_wait_cmd_ioc(struct psp_device *psp, unsigned int timeout, + unsigned int *reg) +{ + unsigned long jiffie_timeout = timeout; + long ret; + + jiffie_timeout *= HZ; + + ret = wait_event_interruptible_timeout(psp->int_queue, psp->int_rcvd, + jiffie_timeout); + if (ret <= 0) { + dev_err(psp->dev, "psp command timed out\n"); + return -ETIMEDOUT; + } + + psp->int_rcvd = 0; + + *reg = ioread32(psp->io_regs + PSP_CMDRESP); + + return 0; +} + +static int psp_wait_cmd(struct psp_device *psp, unsigned int timeout, + unsigned int *reg) +{ + return (*reg & PSP_CMDRESP_IOC) ? psp_wait_cmd_ioc(psp, timeout, reg) + : psp_wait_cmd_poll(psp, timeout, reg); +} + +static int psp_issue_cmd(enum psp_cmd cmd, void *data, unsigned int timeout, + int *psp_ret) +{ + struct psp_device *psp = psp_get_master_device(); + unsigned int phys_lsb, phys_msb; + unsigned int reg, ret; + + if (psp_ret) + *psp_ret = 0; + + if (!psp) + return -ENODEV; + + if (!psp->sev_enabled) + return -ENOTSUPP; + + /* Set the physical address for the PSP */ + phys_lsb = data ? lower_32_bits(__psp_pa(data)) : 0; + phys_msb = data ? upper_32_bits(__psp_pa(data)) : 0; + + /* Only one command at a time... */ + mutex_lock(&psp_cmd_mutex); + + iowrite32(phys_lsb, psp->io_regs + PSP_CMDBUFF_ADDR_LO); + iowrite32(phys_msb, psp->io_regs + PSP_CMDBUFF_ADDR_HI); + wmb(); + + reg = cmd; + reg <<= PSP_CMDRESP_CMD_SHIFT; + reg |= psp_poll ? 0 : PSP_CMDRESP_IOC; + iowrite32(reg, psp->io_regs + PSP_CMDRESP); + + ret = psp_wait_cmd(psp, timeout, ®); + if (ret) + goto unlock; + + if (psp_ret) + *psp_ret = reg & PSP_CMDRESP_ERR_MASK; + + if (reg & PSP_CMDRESP_ERR_MASK) { + dev_err(psp->dev, "psp command %u failed (%#010x)\n", cmd, reg & PSP_CMDRESP_ERR_MASK); + ret = -EIO; + } + +unlock: + mutex_unlock(&psp_cmd_mutex); + + return ret; +} + +int psp_platform_init(struct psp_data_init *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_INIT, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_platform_init); + +int psp_platform_shutdown(int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_SHUTDOWN, NULL, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_platform_shutdown); + +int psp_platform_status(struct psp_data_status *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_PLATFORM_STATUS, data, + PSP_DEFAULT_TIMEOUT, psp_ret); +} +EXPORT_SYMBOL_GPL(psp_platform_status); + +int psp_guest_launch_start(struct psp_data_launch_start *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_LAUNCH_START, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_launch_start); + +int psp_guest_launch_update(struct psp_data_launch_update *data, + unsigned int timeout, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_LAUNCH_UPDATE, data, timeout, psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_launch_update); + +int psp_guest_launch_finish(struct psp_data_launch_finish *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_LAUNCH_FINISH, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_launch_finish); + +int psp_guest_activate(struct psp_data_activate *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_ACTIVATE, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_activate); + +int psp_guest_deactivate(struct psp_data_deactivate *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_DEACTIVATE, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_deactivate); + +int psp_guest_df_flush(int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_DF_FLUSH, NULL, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_df_flush); + +int psp_guest_decommission(struct psp_data_decommission *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_DECOMMISSION, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_decommission); + +int psp_guest_status(struct psp_data_guest_status *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_GUEST_STATUS, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_status); + +int psp_dbg_decrypt(struct psp_data_dbg *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_DBG_DECRYPT, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_dbg_decrypt); + +int psp_dbg_encrypt(struct psp_data_dbg *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_DBG_ENCRYPT, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_dbg_encrypt); + +int psp_guest_receive_start(struct psp_data_receive_start *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_RECEIVE_START, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_receive_start); + +int psp_guest_receive_update(struct psp_data_receive_update *data, + unsigned int timeout, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_RECEIVE_UPDATE, data, timeout, psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_receive_update); + +int psp_guest_receive_finish(struct psp_data_receive_finish *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_RECEIVE_FINISH, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_receive_finish); + +int psp_guest_send_start(struct psp_data_send_start *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_SEND_START, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_send_start); + +int psp_guest_send_update(struct psp_data_send_update *data, + unsigned int timeout, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_SEND_UPDATE, data, timeout, psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_send_update); + +int psp_guest_send_finish(struct psp_data_send_finish *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_SEND_FINISH, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_guest_send_finish); + +int psp_platform_pdh_gen(int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_PDH_GEN, NULL, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_platform_pdh_gen); + +int psp_platform_pdh_cert_export(struct psp_data_pdh_cert_export *data, + int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_PDH_CERT_EXPORT, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_platform_pdh_cert_export); + +int psp_platform_pek_gen(int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_PEK_GEN, NULL, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_platform_pek_gen); + +int psp_platform_pek_cert_import(struct psp_data_pek_cert_import *data, + int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_PEK_CERT_IMPORT, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_platform_pek_cert_import); + +int psp_platform_pek_csr(struct psp_data_pek_csr *data, int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_PEK_CSR, data, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_platform_pek_csr); + +int psp_platform_factory_reset(int *psp_ret) +{ + return psp_issue_cmd(PSP_CMD_FACTORY_RESET, NULL, PSP_DEFAULT_TIMEOUT, + psp_ret); +} +EXPORT_SYMBOL_GPL(psp_platform_factory_reset); + +static int psp_copy_to_user(void __user *argp, void *data, size_t size) +{ + int ret = 0; + + if (copy_to_user(argp, data, size)) + ret = -EFAULT; + free_pages_exact(data, size); + + return ret; +} + +static void *psp_copy_from_user(void __user *argp, size_t *size) +{ + u32 buffer_len; + void *data; + + if (copy_from_user(&buffer_len, argp, sizeof(buffer_len))) + return ERR_PTR(-EFAULT); + + data = alloc_pages_exact(buffer_len, GFP_KERNEL | __GFP_ZERO); + if (!data) + return ERR_PTR(-ENOMEM); + *size = buffer_len; + + if (copy_from_user(data, argp, buffer_len)) { + free_pages_exact(data, *size); + return ERR_PTR(-EFAULT); + } + + return data; +} + +static long psp_ioctl(struct file *file, unsigned int ioctl, unsigned long arg) +{ + int ret = -EFAULT; + void *data = NULL; + size_t buffer_len = 0; + void __user *argp = (void __user *)arg; + struct psp_issue_cmd input; + + if (ioctl != PSP_ISSUE_CMD) + return -EINVAL; + + /* get input parameters */ + if (copy_from_user(&input, argp, sizeof(struct psp_issue_cmd))) + return -EFAULT; + + if (input.cmd > PSP_CMD_MAX) + return -EINVAL; + + switch (input.cmd) { + + case PSP_CMD_INIT: { + struct psp_data_init *init; + + data = psp_copy_from_user((void*)input.opaque, &buffer_len); + if (IS_ERR(data)) + break; + + init = data; + ret = psp_platform_init(init, &input.psp_ret); + break; + } + case PSP_CMD_SHUTDOWN: { + ret = psp_platform_shutdown(&input.psp_ret); + break; + } + case PSP_CMD_FACTORY_RESET: { + ret = psp_platform_factory_reset(&input.psp_ret); + break; + } + case PSP_CMD_PLATFORM_STATUS: { + struct psp_data_status *status; + + data = psp_copy_from_user((void*)input.opaque, &buffer_len); + if (IS_ERR(data)) + break; + + status = data; + ret = psp_platform_status(status, &input.psp_ret); + break; + } + case PSP_CMD_PEK_GEN: { + ret = psp_platform_pek_gen(&input.psp_ret); + break; + } + case PSP_CMD_PEK_CSR: { + struct psp_data_pek_csr *pek_csr; + + data = psp_copy_from_user((void*)input.opaque, &buffer_len); + if (IS_ERR(data)) + break; + + pek_csr = data; + ret = psp_platform_pek_csr(pek_csr, &input.psp_ret); + break; + } + case PSP_CMD_PEK_CERT_IMPORT: { + struct psp_data_pek_cert_import *import; + + data = psp_copy_from_user((void*)input.opaque, &buffer_len); + if (IS_ERR(data)) + break; + + import = data; + ret = psp_platform_pek_cert_import(import, &input.psp_ret); + break; + } + case PSP_CMD_PDH_GEN: { + ret = psp_platform_pdh_gen(&input.psp_ret); + break; + } + case PSP_CMD_PDH_CERT_EXPORT: { + struct psp_data_pdh_cert_export *export; + + data = psp_copy_from_user((void*)input.opaque, &buffer_len); + if (IS_ERR(data)) + break; + + export = data; + ret = psp_platform_pdh_cert_export(export, &input.psp_ret); + break; + } + default: + ret = -EINVAL; + } + + if (data && psp_copy_to_user((void *)input.opaque, + data, buffer_len)) + ret = -EFAULT; + + if (copy_to_user(argp, &input, sizeof(struct psp_issue_cmd))) + ret = -EFAULT; + + return ret; +} + +static const struct file_operations fops = { + .owner = THIS_MODULE, + .unlocked_ioctl = psp_ioctl, +}; + +int psp_ops_init(struct psp_device *psp) +{ + struct miscdevice *misc = &psp->misc; + + misc->minor = MISC_DYNAMIC_MINOR; + misc->name = psp->name; + misc->fops = &fops; + + return misc_register(misc); +} + +void psp_ops_exit(struct psp_device *psp) +{ + misc_deregister(&psp->misc); +} diff --git a/drivers/crypto/psp/psp-pci.c b/drivers/crypto/psp/psp-pci.c new file mode 100644 index 0000000..2b4c379 --- /dev/null +++ b/drivers/crypto/psp/psp-pci.c @@ -0,0 +1,376 @@ +/* + * AMD Cryptographic Coprocessor (CCP) driver + * + * Copyright (C) 2016 Advanced Micro Devices, Inc. + * + * Author: Tom Lendacky <thomas.lendacky@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> +#include <linux/dma-mapping.h> +#include <linux/sched.h> +#include <linux/interrupt.h> + +#include "psp-dev.h" + +#define IO_BAR 2 +#define IO_OFFSET 0x10500 + +#define MSIX_VECTORS 2 + +struct psp_msix { + u32 vector; + char name[16]; +}; + +struct psp_pci { + struct pci_dev *pdev; + int msix_count; + struct psp_msix msix[MSIX_VECTORS]; +}; + +static int psp_get_msix_irqs(struct psp_device *psp) +{ + struct psp_pci *psp_pci = psp->dev_specific; + struct device *dev = psp->dev; + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + struct msix_entry msix_entry[MSIX_VECTORS]; + unsigned int name_len = sizeof(psp_pci->msix[0].name) - 1; + int v, ret; + + for (v = 0; v < ARRAY_SIZE(msix_entry); v++) + msix_entry[v].entry = v; + + ret = pci_enable_msix_range(pdev, msix_entry, 1, v); + if (ret < 0) + return ret; + + psp_pci->msix_count = ret; + for (v = 0; v < psp_pci->msix_count; v++) { + /* Set the interrupt names and request the irqs */ + snprintf(psp_pci->msix[v].name, name_len, "%s-%u", psp->name, v); + psp_pci->msix[v].vector = msix_entry[v].vector; + ret = request_irq(psp_pci->msix[v].vector, psp_irq_handler, + 0, psp_pci->msix[v].name, dev); + if (ret) { + dev_notice(dev, "unable to allocate MSI-X IRQ (%d)\n", + ret); + goto e_irq; + } + } + + return 0; + +e_irq: + while (v--) + free_irq(psp_pci->msix[v].vector, dev); + pci_disable_msix(pdev); + psp_pci->msix_count = 0; + + return ret; +} + +static int psp_get_msi_irq(struct psp_device *psp) +{ + struct device *dev = psp->dev; + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + int ret; + + ret = pci_enable_msi(pdev); + if (ret) + return ret; + + psp->irq = pdev->irq; + ret = request_irq(psp->irq, psp_irq_handler, 0, psp->name, dev); + if (ret) { + dev_notice(dev, "unable to allocate MSI IRQ (%d)\n", ret); + goto e_msi; + } + + return 0; + +e_msi: + pci_disable_msi(pdev); + + return ret; +} + +static int psp_get_irqs(struct psp_device *psp) +{ + struct device *dev = psp->dev; + int ret; + + ret = psp_get_msix_irqs(psp); + if (!ret) + return 0; + + /* Couldn't get MSI-X vectors, try MSI */ + dev_notice(dev, "could not enable MSI-X (%d), trying MSI\n", ret); + ret = psp_get_msi_irq(psp); + if (!ret) + return 0; + + /* Couldn't get MSI interrupt */ + dev_notice(dev, "could not enable MSI (%d), trying PCI\n", ret); + + return ret; +} + +void psp_free_irqs(struct psp_device *psp) +{ + struct psp_pci *psp_pci = psp->dev_specific; + struct device *dev = psp->dev; + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + + if (psp_pci->msix_count) { + while (psp_pci->msix_count--) + free_irq(psp_pci->msix[psp_pci->msix_count].vector, + dev); + pci_disable_msix(pdev); + } else { + free_irq(psp->irq, dev); + pci_disable_msi(pdev); + } +} + +static bool psp_is_master(struct psp_device *cur, struct psp_device *new) +{ + struct psp_pci *psp_pci_cur, *psp_pci_new; + struct pci_dev *pdev_cur, *pdev_new; + + psp_pci_cur = cur->dev_specific; + psp_pci_new = new->dev_specific; + + pdev_cur = psp_pci_cur->pdev; + pdev_new = psp_pci_new->pdev; + + if (pdev_new->bus->number < pdev_cur->bus->number) + return true; + + if (PCI_SLOT(pdev_new->devfn) < PCI_SLOT(pdev_cur->devfn)) + return true; + + if (PCI_FUNC(pdev_new->devfn) < PCI_FUNC(pdev_cur->devfn)) + return true; + + return false; +} + +static struct psp_device *psp_get_master(struct list_head *list) +{ + struct psp_device *psp, *tmp; + + psp = NULL; + list_for_each_entry(tmp, list, entry) { + if (!psp || psp_is_master(psp, tmp)) + psp = tmp; + } + + return psp; +} + +static int psp_find_mmio_area(struct psp_device *psp) +{ + struct device *dev = psp->dev; + struct pci_dev *pdev = container_of(dev, struct pci_dev, dev); + unsigned long io_flags; + + io_flags = pci_resource_flags(pdev, IO_BAR); + if (io_flags & IORESOURCE_MEM) + return IO_BAR; + + return -EIO; +} + +static int psp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id) +{ + struct psp_device *psp; + struct psp_pci *psp_pci; + struct device *dev = &pdev->dev; + unsigned int bar; + int ret; + + ret = -ENOMEM; + psp = psp_alloc_struct(dev); + if (!psp) + goto e_err; + + psp_pci = devm_kzalloc(dev, sizeof(*psp_pci), GFP_KERNEL); + if (!psp_pci) { + ret = -ENOMEM; + goto e_err; + } + psp_pci->pdev = pdev; + psp->dev_specific = psp_pci; + psp->get_irq = psp_get_irqs; + psp->free_irq = psp_free_irqs; + psp->get_master = psp_get_master; + + ret = pci_request_regions(pdev, PSP_DRIVER_NAME); + if (ret) { + dev_err(dev, "pci_request_regions failed (%d)\n", ret); + goto e_err; + } + + ret = pci_enable_device(pdev); + if (ret) { + dev_err(dev, "pci_enable_device failed (%d)\n", ret); + goto e_regions; + } + + pci_set_master(pdev); + + ret = psp_find_mmio_area(psp); + if (ret < 0) + goto e_device; + bar = ret; + + ret = -EIO; + psp->io_map = pci_iomap(pdev, bar, 0); + if (!psp->io_map) { + dev_err(dev, "pci_iomap failed\n"); + goto e_device; + } + psp->io_regs = psp->io_map + IO_OFFSET; + + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48)); + if (ret) { + ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(dev, "dma_set_mask_and_coherent failed (%d)\n", + ret); + goto e_iomap; + } + } + + dev_set_drvdata(dev, psp); + + ret = psp_init(psp); + if (ret) + goto e_iomap; + + dev_notice(dev, "enabled\n"); + + return 0; + +e_iomap: + pci_iounmap(pdev, psp->io_map); + +e_device: + pci_disable_device(pdev); + +e_regions: + pci_release_regions(pdev); + +e_err: + dev_notice(dev, "initialization failed\n"); + return ret; +} + +static void psp_pci_remove(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + struct psp_device *psp = dev_get_drvdata(dev); + + if (!psp) + return; + + psp_destroy(psp); + + pci_iounmap(pdev, psp->io_map); + + pci_disable_device(pdev); + + pci_release_regions(pdev); + + dev_notice(dev, "disabled\n"); +} + +#if 0 +#ifdef CONFIG_PM +static int ccp_pci_suspend(struct pci_dev *pdev, pm_message_t state) +{ + struct device *dev = &pdev->dev; + struct ccp_device *ccp = dev_get_drvdata(dev); + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&ccp->cmd_lock, flags); + + ccp->suspending = 1; + + /* Wake all the queue kthreads to prepare for suspend */ + for (i = 0; i < ccp->cmd_q_count; i++) + wake_up_process(ccp->cmd_q[i].kthread); + + spin_unlock_irqrestore(&ccp->cmd_lock, flags); + + /* Wait for all queue kthreads to say they're done */ + while (!ccp_queues_suspended(ccp)) + wait_event_interruptible(ccp->suspend_queue, + ccp_queues_suspended(ccp)); + + return 0; +} + +static int ccp_pci_resume(struct pci_dev *pdev) +{ + struct device *dev = &pdev->dev; + struct ccp_device *ccp = dev_get_drvdata(dev); + unsigned long flags; + unsigned int i; + + spin_lock_irqsave(&ccp->cmd_lock, flags); + + ccp->suspending = 0; + + /* Wake up all the kthreads */ + for (i = 0; i < ccp->cmd_q_count; i++) { + ccp->cmd_q[i].suspended = 0; + wake_up_process(ccp->cmd_q[i].kthread); + } + + spin_unlock_irqrestore(&ccp->cmd_lock, flags); + + return 0; +} +#endif +#endif + +static const struct pci_device_id psp_pci_table[] = { + { PCI_VDEVICE(AMD, 0x1456), }, + /* Last entry must be zero */ + { 0, } +}; +MODULE_DEVICE_TABLE(pci, psp_pci_table); + +static struct pci_driver psp_pci_driver = { + .name = PSP_DRIVER_NAME, + .id_table = psp_pci_table, + .probe = psp_pci_probe, + .remove = psp_pci_remove, +#if 0 +#ifdef CONFIG_PM + .suspend = ccp_pci_suspend, + .resume = ccp_pci_resume, +#endif +#endif +}; + +int psp_pci_init(void) +{ + return pci_register_driver(&psp_pci_driver); +} + +void psp_pci_exit(void) +{ + pci_unregister_driver(&psp_pci_driver); +} diff --git a/include/linux/ccp-psp.h b/include/linux/ccp-psp.h new file mode 100644 index 0000000..b5e791c --- /dev/null +++ b/include/linux/ccp-psp.h @@ -0,0 +1,833 @@ +/* + * AMD Secure Processor (PSP) driver + * + * Copyright (C) 2016 Advanced Micro Devices, Inc. + * + * Author: Tom Lendacky <thomas.lendacky@xxxxxxx> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __CPP_PSP_H__ +#define __CPP_PSP_H__ + +#include <uapi/linux/ccp-psp.h> + +#ifdef CONFIG_X86 +#include <asm/mem_encrypt.h> + +#define __psp_pa(x) __sme_pa(x) +#else +#define __psp_pa(x) __pa(x) +#endif + +/** + * struct psp_data_activate - PSP ACTIVATE command parameters + * @hdr: command header + * @handle: handle of the VM to activate + * @asid: asid assigned to the VM + */ +struct __attribute__ ((__packed__)) psp_data_activate { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ + u32 asid; /* In */ +}; + +/** + * struct psp_data_deactivate - PSP DEACTIVATE command parameters + * @hdr: command header + * @handle: handle of the VM to deactivate + */ +struct __attribute__ ((__packed__)) psp_data_deactivate { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ +}; + +/** + * struct psp_data_launch_start - PSP LAUNCH_START command parameters + * @hdr: command header + * @handle: handle assigned to the VM + * @flags: configuration flags for the VM + * @policy: policy information for the VM + * @dh_pub_qx: the Qx parameter of the VM owners ECDH public key + * @dh_pub_qy: the Qy parameter of the VM owners ECDH public key + * @nonce: nonce generated by the VM owner + */ +struct __attribute__ ((__packed__)) psp_data_launch_start { + struct psp_data_header hdr; + u32 handle; /* In/Out */ + u32 flags; /* In */ + u32 policy; /* In */ + u8 dh_pub_qx[32]; /* In */ + u8 dh_pub_qy[32]; /* In */ + u8 nonce[16]; /* In */ +}; + +/** + * struct psp_data_launch_update - PSP LAUNCH_UPDATE command parameters + * @hdr: command header + * @handle: handle of the VM to update + * @length: length of memory to be encrypted + * @address: physical address of memory region to encrypt + */ +struct __attribute__ ((__packed__)) psp_data_launch_update { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ + u64 address; /* In */ + u32 length; /* In */ +}; + +/** + * struct psp_data_launch_vcpus - PSP LAUNCH_FINISH VCPU state information + * @state_length: length of the VCPU state information to measure + * @state_mask_addr: mask of the bytes within the VCPU state information + * to use in the measurment + * @state_count: number of VCPUs to measure + * @state_addr: physical address of the VCPU state (VMCB) + */ +struct __attribute__ ((__packed__)) psp_data_launch_vcpus { + u32 state_length; /* In */ + u64 state_mask_addr; /* In */ + u32 state_count; /* In */ + u64 state_addr[]; /* In */ +}; + +/** + * struct psp_data_launch_finish - PSP LAUNCH_FINISH command parameters + * @hdr: command header + * @handle: handle of the VM to process + * @measurement: the measurement of the encrypted VM memory areas + * @vcpus: the VCPU state information to include in the measurement + */ +struct __attribute__ ((__packed__)) psp_data_launch_finish { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ + u8 measurement[32]; /* In/Out */ + struct psp_data_launch_vcpus vcpus; /* In */ +}; + +/** + * struct psp_data_decommission - PSP DECOMMISSION command parameters + * @hdr: command header + * @handle: handle of the VM to decommission + */ +struct __attribute__ ((__packed__)) psp_data_decommission { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ +}; + +/** + * struct psp_data_guest_status - PSP GUEST_STATUS command parameters + * @hdr: command header + * @handle: handle of the VM to retrieve status + * @policy: policy information for the VM + * @asid: current ASID of the VM + * @state: current state of the VM + */ +struct __attribute__ ((__packed__)) psp_data_guest_status { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ + u32 policy; /* Out */ + u32 asid; /* Out */ + u8 state; /* Out */ +}; + +/** + * struct psp_data_dbg - PSP DBG_ENCRYPT/DBG_DECRYPT command parameters + * @hdr: command header + * @handle: handle of the VM to perform debug operation + * @src_addr: source address of data to operate on + * @dst_addr: destination address of data to operate on + * @length: length of data to operate on + */ +struct __attribute__ ((__packed__)) psp_data_dbg { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ + u64 src_addr; /* In */ + u64 dst_addr; /* In */ + u32 length; /* In */ +}; + +/** + * struct psp_data_receive_start - PSP RECEIVE_START command parameters + * + * @hdr: command header + * @handle: handle of the VM to receiving the guest + * @flags: flags for the receive process + * @policy: guest policy flags + * @policy_meas: HMAC of policy keypad + * @wrapped_tek: wrapped transport encryption key + * @wrapped_tik: wrapped transport integrity key + * @ten: transport encryption nonce + * @dh_pub_qx: qx parameter of the origin's ECDH public key + * @dh_pub_qy: qy parameter of the origin's ECDH public key + * @nonce: nonce generated by the origin + */ +struct __attribute__((__packed__)) psp_data_receive_start { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In/Out */ + u32 flags; /* In */ + u32 policy; /* In */ + u8 policy_meas[32]; /* In */ + u8 wrapped_tek[24]; /* In */ + u8 reserved1[8]; + u8 wrapped_tik[24]; /* In */ + u8 reserved2[8]; + u8 ten[16]; /* In */ + u8 dh_pub_qx[32]; /* In */ + u8 dh_pub_qy[32]; /* In */ + u8 nonce[16]; /* In */ +}; + +/** + * struct psp_receive_update - PSP RECEIVE_UPDATE command parameters + * + * @hdr: command header + * @handle: handle of the VM to receiving the guest + * @iv: initialization vector for this blob of memory + * @count: number of memory areas to be encrypted + * @length: length of memory to be encrypted + * @address: physical address of memory region to encrypt + */ +struct __attribute__((__packed__)) psp_data_receive_update { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ + u8 iv[16]; /* In */ + u64 address; /* In */ + u32 length; /* In */ +}; + +/** + * struct psp_data_receive_finish - PSP RECEIVE_FINISH command parameters + * @hdr: command header + * @handle: handle of the VM to process + * @measurement: the measurement of the transported guest + */ +struct __attribute__ ((__packed__)) psp_data_receive_finish { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ + u8 measurement[32]; /* In */ +}; + +/** + * struct psp_data_send_start - PSP SEND_START command parameters + * @hdr: command header + * @nonce: nonce generated by firmware + * @policy: guest policy flags + * @policy_meas: HMAC of policy keyed with TIK + * @wrapped_tek: wrapped transport encryption key + * @wrapped_tik: wrapped transport integrity key + * @ten: transport encryrption nonce + * @iv: the IV of transport encryption block + * @handle: handle of the VM to process + * @flags: flags for send command + * @major: API major number + * @minor: API minor number + * @serial: platform serial number + * @dh_pub_qx: the Qx parameter of the target DH public key + * @dh_pub_qy: the Qy parameter of the target DH public key + * @pek_sig_r: the r component of the PEK signature + * @pek_sig_s: the s component of the PEK signature + * @cek_sig_r: the r component of the CEK signature + * @cek_sig_s: the s component of the CEK signature + * @cek_pub_qx: the Qx parameter of the CEK public key + * @cek_pub_qy: the Qy parameter of the CEK public key + * @ask_sig_r: the r component of the ASK signature + * @ask_sig_s: the s component of the ASK signature + * @ncerts: number of certificates in certificate chain + * @cert_len: length of certificates + * @certs: certificate in chain + */ + +struct __attribute__((__packed__)) psp_data_send_start { + struct psp_data_header hdr; /* In/Out */ + u8 nonce[16]; /* Out */ + u32 policy; /* Out */ + u8 policy_meas[32]; /* Out */ + u8 wrapped_tek[24]; /* Out */ + u8 reserved1[8]; + u8 wrapped_tik[24]; /* Out */ + u8 reserved2[8]; + u8 ten[16]; /* Out */ + u8 iv[16]; /* Out */ + u32 handle; /* In */ + u32 flags; /* In */ + u8 api_major; /* In */ + u8 api_minor; /* In */ + u8 reserved3[2]; + u32 serial; /* In */ + u8 dh_pub_qx[32]; /* In */ + u8 dh_pub_qy[32]; /* In */ + u8 pek_sig_r[32]; /* In */ + u8 pek_sig_s[32]; /* In */ + u8 cek_sig_r[32]; /* In */ + u8 cek_sig_s[32]; /* In */ + u8 cek_pub_qx[32]; /* In */ + u8 cek_pub_qy[32]; /* In */ + u8 ask_sig_r[32]; /* In */ + u8 ask_sig_s[32]; /* In */ + u32 ncerts; /* In */ + u32 cert_length; /* In */ + u8 certs[]; /* In */ +}; + +/** + * struct psp_data_send_update - PSP SEND_UPDATE command parameters + * + * @hdr: command header + * @handle: handle of the VM to receiving the guest + * @len: length of memory region to encrypt + * @src_addr: physical address of memory region to encrypt from + * @dst_addr: physical address of memory region to encrypt to + */ +struct __attribute__((__packed__)) psp_data_send_update { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ + u64 src_addr; /* In */ + u64 dst_addr; /* In */ + u32 length; /* In */ +}; + +/** + * struct psp_data_send_finish - PSP SEND_FINISH command parameters + * @hdr: command header + * @handle: handle of the VM to process + * @measurement: the measurement of the transported guest + */ +struct __attribute__ ((__packed__)) psp_data_send_finish { + struct psp_data_header hdr; /* In/Out */ + u32 handle; /* In */ + u8 measurement[32]; /* Out */ +}; + +#if defined(CONFIG_CRYPTO_DEV_PSP_DD) || \ + defined(CONFIG_CRYPTO_DEV_PSP_DD_MODULE) + +/** + * psp_platform_init - perform PSP INIT command + * + * @init: psp_data_init structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_platform_init(struct psp_data_init *init, int *psp_ret); + +/** + * psp_platform_shutdown - perform PSP SHUTDOWN command + * + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_platform_shutdown(int *psp_ret); + +/** + * psp_platform_status - perform PSP PLATFORM_STATUS command + * + * @init: psp_data_status structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_platform_status(struct psp_data_status *status, int *psp_ret); + +/** + * psp_guest_launch_start - perform PSP LAUNCH_START command + * + * @start: psp_data_launch_start structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_launch_start(struct psp_data_launch_start *start, int *psp_ret); + +/** + * psp_guest_launch_update - perform PSP LAUNCH_UPDATE command + * + * @update: psp_data_launch_update structure to be processed + * @timeout: command timeout + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_launch_update(struct psp_data_launch_update *update, + unsigned int timeout, int *psp_ret); + +/** + * psp_guest_launch_finish - perform PSP LAUNCH_FINISH command + * + * @finish: psp_data_launch_finish structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_launch_finish(struct psp_data_launch_finish *finish, int *psp_ret); + +/** + * psp_guest_activate - perform PSP ACTIVATE command + * + * @activate: psp_data_activate structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_activate(struct psp_data_activate *activate, int *psp_ret); + +/** + * psp_guest_deactivate - perform PSP DEACTIVATE command + * + * @deactivate: psp_data_deactivate structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_deactivate(struct psp_data_deactivate *deactivate, int *psp_ret); + +/** + * psp_guest_df_flush - perform PSP DF_FLUSH command + * + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_df_flush(int *psp_ret); + +/** + * psp_guest_decommission - perform PSP DECOMMISSION command + * + * @decommission: psp_data_decommission structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_decommission(struct psp_data_decommission *decommission, + int *psp_ret); + +/** + * psp_guest_status - perform PSP GUEST_STATUS command + * + * @status: psp_data_guest_status structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_status(struct psp_data_guest_status *status, int *psp_ret); + +/** + * psp_dbg_decrypt - perform PSP DBG_DECRYPT command + * + * @dbg: psp_data_dbg structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_dbg_decrypt(struct psp_data_dbg *dbg, int *psp_ret); + +/** + * psp_dbg_encrypt - perform PSP DBG_ENCRYPT command + * + * @dbg: psp_data_dbg structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_dbg_encrypt(struct psp_data_dbg *dbg, int *psp_ret); + +/** + * psp_guest_receive_start - perform PSP RECEIVE_START command + * + * @start: psp_data_receive_start structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_receive_start(struct psp_data_receive_start *start, int *psp_ret); + +/** + * psp_guest_receive_update - perform PSP RECEIVE_UPDATE command + * + * @update: psp_data_receive_update structure to be processed + * @timeout: command timeout + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_receive_update(struct psp_data_receive_update *update, + unsigned int timeout, int *psp_ret); + +/** + * psp_guest_receive_finish - perform PSP RECEIVE_FINISH command + * + * @finish: psp_data_receive_finish structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_receive_finish(struct psp_data_receive_finish *finish, + int *psp_ret); + +/** + * psp_guest_send_start - perform PSP RECEIVE_START command + * + * @start: psp_data_send_start structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_send_start(struct psp_data_send_start *start, int *psp_ret); + +/** + * psp_guest_send_update - perform PSP RECEIVE_UPDATE command + * + * @update: psp_data_send_update structure to be processed + * @timeout: command timeout + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_send_update(struct psp_data_send_update *update, + unsigned int timeout, int *psp_ret); + +/** + * psp_guest_send_finish - perform PSP RECEIVE_FINISH command + * + * @finish: psp_data_send_finish structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_guest_send_finish(struct psp_data_send_finish *finish, + int *psp_ret); + +/** + * psp_platform_pdh_gen - perform PSP PDH_GEN command + * + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_platform_pdh_gen(int *psp_ret); + +/** + * psp_platform_pdh_cert_export - perform PSP PDH_CERT_EXPORT command + * + * @data: psp_data_platform_pdh_cert_export structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_platform_pdh_cert_export(struct psp_data_pdh_cert_export *data, + int *psp_ret); + +/** + * psp_platform_pek_gen - perform PSP PEK_GEN command + * + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_platform_pek_gen(int *psp_ret); + +/** + * psp_platform_pek_cert_import - perform PSP PEK_CERT_IMPORT command + * + * @data: psp_data_platform_pek_cert_import structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_platform_pek_cert_import(struct psp_data_pek_cert_import *data, + int *psp_ret); + +/** + * psp_platform_pek_csr - perform PSP PEK_CSR command + * + * @data: psp_data_platform_pek_csr structure to be processed + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_platform_pek_csr(struct psp_data_pek_csr *data, int *psp_ret); + +/** + * psp_platform_factory_reset - perform PSP FACTORY_RESET command + * + * @psp_ret: PSP command return code + * + * Returns: + * 0 if the PSP successfully processed the command + * -%ENODEV if the PSP device is not available + * -%ENOTSUPP if the PSP does not support SEV + * -%ETIMEDOUT if the PSP command timed out + * -%EIO if the PSP returned a non-zero return code + */ +int psp_platform_factory_reset(int *psp_ret); + +#else /* CONFIG_CRYPTO_DEV_PSP_DD is not enabled */ + +static inline int psp_platform_status(struct psp_data_status *status, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_platform_init(struct psp_data_init *init, int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_platform_shutdown(int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_launch_start(struct psp_data_launch_start *start, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_launch_update(struct psp_data_launch_update *update, + unsigned int timeout, int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_launch_finish(struct psp_data_launch_finish *finish, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_activate(struct psp_data_activate *activate, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_deactivate(struct psp_data_deactivate *deactivate, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_df_flush(int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_decommission(struct psp_data_decommission *decommission, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_status(struct psp_data_guest_status *status, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_dbg_decrypt(struct psp_data_dbg *dbg, int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_dbg_encrypt(struct psp_data_dbg *dbg, int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_receive_start(struct psp_data_receive_start *start, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_receive_update(struct psp_data_receive_update *update, + unsigned int timeout, int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_receive_finish(struct psp_data_receive_finish *finish, + int *psp_ret) +{ + return -ENODEV; +} +static inline int psp_guest_send_start(struct psp_data_send_start *start, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_send_update(struct psp_data_send_update *update, + unsigned int timeout, int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_guest_send_finish(struct psp_data_send_finish *finish, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_platform_pdh_gen(int *psp_ret) +{ + return -ENODEV; +} + +int psp_platform_pdh_cert_export(struct psp_data_pdh_cert_export *data, + int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_platform_pek_gen(int *psp_ret) +{ + return -ENODEV; +} + +int psp_platform_pek_cert_import(struct psp_data_pek_cert_import *data, + int *psp_ret) +{ + return -ENODEV; +} + +int psp_platform_pek_csr(struct psp_data_pek_csr *data, int *psp_ret) +{ + return -ENODEV; +} + +static inline int psp_platform_factory_reset(int *psp_ret) +{ + return -ENODEV; +} + +#endif /* CONFIG_CRYPTO_DEV_PSP_DD */ + +#endif /* __CPP_PSP_H__ */ diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild index 185f8ea..af2511a 100644 --- a/include/uapi/linux/Kbuild +++ b/include/uapi/linux/Kbuild @@ -470,3 +470,4 @@ header-y += xilinx-v4l2-controls.h header-y += zorro.h header-y += zorro_ids.h header-y += userfaultfd.h +header-y += ccp-psp.h diff --git a/include/uapi/linux/ccp-psp.h b/include/uapi/linux/ccp-psp.h new file mode 100644 index 0000000..e780b46 --- /dev/null +++ b/include/uapi/linux/ccp-psp.h @@ -0,0 +1,182 @@ +#ifndef _UAPI_LINUX_CCP_PSP_ +#define _UAPI_LINUX_CCP_PSP_ + +/* + * Userspace interface to communicated with CCP-PSP driver. + */ + +#include <linux/types.h> +#include <linux/ioctl.h> + +/** + * struct psp_data_header - Common PSP communication header + * @buffer_len: length of the buffer supplied to the PSP + */ + +struct __attribute__ ((__packed__)) psp_data_header { + __u32 buffer_len; /* In/Out */ +}; + +/** + * struct psp_data_init - PSP INIT command parameters + * @hdr: command header + * @flags: processing flags + */ +struct __attribute__ ((__packed__)) psp_data_init { + struct psp_data_header hdr; + __u32 flags; /* In */ +}; + +/** + * struct psp_data_status - PSP PLATFORM_STATUS command parameters + * @hdr: command header + * @major: major API version + * @minor: minor API version + * @state: platform state + * @cert_status: bit fields describing certificate status + * @flags: platform flags + * @guest_count: number of active guests + */ +struct __attribute__ ((__packed__)) psp_data_status { + struct psp_data_header hdr; + __u8 api_major; /* Out */ + __u8 api_minor; /* Out */ + __u8 state; /* Out */ + __u8 cert_status; /* Out */ + __u32 flags; /* Out */ + __u32 guest_count; /* Out */ +}; + +/** + * struct psp_data_pek_csr - PSP PEK_CSR command parameters + * @hdr: command header + * @csr - certificate signing request formatted with PKCS + */ +struct __attribute__((__packed__)) psp_data_pek_csr { + struct psp_data_header hdr; /* In/Out */ + __u8 csr[]; /* Out */ +}; + +/** + * struct psp_data_cert_import - PSP PEK_CERT_IMPORT command parameters + * @hdr: command header + * @ncerts: number of certificates in the chain + * @cert_len: length of certificates + * @certs: certificate chain starting with PEK and end with CA certificate + */ +struct __attribute__((__packed__)) psp_data_pek_cert_import { + struct psp_data_header hdr; /* In/Out */ + __u32 ncerts; /* In */ + __u32 cert_len; /* In */ + __u8 certs[]; /* In */ +}; + +/** + * struct psp_data_pdh_cert_export - PSP PDH_CERT_EXPORT command parameters + * @hdr: command header + * @major: API major number + * @minor: API minor number + * @serial: platform serial number + * @pdh_pub_qx: the Qx parameter of the target PDH public key + * @pdh_pub_qy: the Qy parameter of the target PDH public key + * @pek_sig_r: the r component of the PEK signature + * @pek_sig_s: the s component of the PEK signature + * @cek_sig_r: the r component of the CEK signature + * @cek_sig_s: the s component of the CEK signature + * @cek_pub_qx: the Qx parameter of the CEK public key + * @cek_pub_qy: the Qy parameter of the CEK public key + * @ncerts: number of certificates in certificate chain + * @cert_len: length of certificates + * @certs: certificate chain starting with PEK and end with CA certificate + */ +struct __attribute__((__packed__)) psp_data_pdh_cert_export { + struct psp_data_header hdr; /* In/Out */ + __u8 api_major; /* Out */ + __u8 api_minor; /* Out */ + __u8 reserved1[2]; + __u32 serial; /* Out */ + __u8 pdh_pub_qx[32]; /* Out */ + __u8 pdh_pub_qy[32]; /* Out */ + __u8 pek_sig_r[32]; /* Out */ + __u8 pek_sig_s[32]; /* Out */ + __u8 cek_sig_r[32]; /* Out */ + __u8 cek_sig_s[32]; /* Out */ + __u8 cek_pub_qx[32]; /* Out */ + __u8 cek_pub_qy[32]; /* Out */ + __u32 ncerts; /* Out */ + __u32 cert_len; /* Out */ + __u8 certs[]; /* Out */ +}; + +/** + * platform and management commands + */ +enum psp_cmd { + PSP_CMD_INIT = 1, + PSP_CMD_LAUNCH_START, + PSP_CMD_LAUNCH_UPDATE, + PSP_CMD_LAUNCH_FINISH, + PSP_CMD_ACTIVATE, + PSP_CMD_DF_FLUSH, + PSP_CMD_SHUTDOWN, + PSP_CMD_FACTORY_RESET, + PSP_CMD_PLATFORM_STATUS, + PSP_CMD_PEK_GEN, + PSP_CMD_PEK_CSR, + PSP_CMD_PEK_CERT_IMPORT, + PSP_CMD_PDH_GEN, + PSP_CMD_PDH_CERT_EXPORT, + PSP_CMD_SEND_START, + PSP_CMD_SEND_UPDATE, + PSP_CMD_SEND_FINISH, + PSP_CMD_RECEIVE_START, + PSP_CMD_RECEIVE_UPDATE, + PSP_CMD_RECEIVE_FINISH, + PSP_CMD_GUEST_STATUS, + PSP_CMD_DEACTIVATE, + PSP_CMD_DECOMMISSION, + PSP_CMD_DBG_DECRYPT, + PSP_CMD_DBG_ENCRYPT, + PSP_CMD_MAX, +}; + +/** + * status code returned by the commands + */ +enum psp_ret_code { + PSP_RET_SUCCESS = 0, + PSP_RET_INVALID_PLATFORM_STATE, + PSP_RET_INVALID_GUEST_STATE, + PSP_RET_INAVLID_CONFIG, + PSP_RET_CMDBUF_TOO_SMALL, + PSP_RET_ALREADY_OWNED, + PSP_RET_INVALID_CERTIFICATE, + PSP_RET_POLICY_FAILURE, + PSP_RET_INACTIVE, + PSP_RET_INVALID_ADDRESS, + PSP_RET_BAD_SIGNATURE, + PSP_RET_BAD_MEASUREMENT, + PSP_RET_ASID_OWNED, + PSP_RET_INVALID_ASID, + PSP_RET_WBINVD_REQUIRED, + PSP_RET_DFFLUSH_REQUIRED, + PSP_RET_INVALID_GUEST, +}; + +/** + * struct psp_issue_cmd - PSP ioctl parameters + * @cmd: PSP commands to execute + * @opaque: pointer to the command structure + * @psp_ret: PSP return code on failure + */ +struct psp_issue_cmd { + __u32 cmd; /* In */ + __u64 opaque; /* In */ + __u32 psp_ret; /* Out */ +}; + +#define PSP_IOC_TYPE 'P' +#define PSP_ISSUE_CMD _IOWR(PSP_IOC_TYPE, 0x0, struct psp_issue_cmd) + +#endif /* _UAPI_LINUX_CCP_PSP_H */ + -- To unsubscribe from this list: send the line "unsubscribe linux-crypto" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html