mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-30 11:41:38 -04:00
KVM changes for 4.16
ARM:
- Include icache invalidation optimizations, improving VM startup time
- Support for forwarded level-triggered interrupts, improving
performance for timers and passthrough platform devices
- A small fix for power-management notifiers, and some cosmetic changes
PPC:
- Add MMIO emulation for vector loads and stores
- Allow HPT guests to run on a radix host on POWER9 v2.2 CPUs without
requiring the complex thread synchronization of older CPU versions
- Improve the handling of escalation interrupts with the XIVE interrupt
controller
- Support decrement register migration
- Various cleanups and bugfixes.
s390:
- Cornelia Huck passed maintainership to Janosch Frank
- Exitless interrupts for emulated devices
- Cleanup of cpuflag handling
- kvm_stat counter improvements
- VSIE improvements
- mm cleanup
x86:
- Hypervisor part of SEV
- UMIP, RDPID, and MSR_SMI_COUNT emulation
- Paravirtualized TLB shootdown using the new KVM_VCPU_PREEMPTED bit
- Allow guests to see TOPOEXT, GFNI, VAES, VPCLMULQDQ, and more AVX512
features
- Show vcpu id in its anonymous inode name
- Many fixes and cleanups
- Per-VCPU MSR bitmaps (already merged through x86/pti branch)
- Stable KVM clock when nesting on Hyper-V (merged through x86/hyperv)
-----BEGIN PGP SIGNATURE-----
iQEcBAABCAAGBQJafvMtAAoJEED/6hsPKofo6YcH/Rzf2RmshrWaC3q82yfIV0Qz
Z8N8yJHSaSdc3Jo6cmiVj0zelwAxdQcyjwlT7vxt5SL2yML+/Q0st9Hc3EgGGXPm
Il99eJEl+2MYpZgYZqV8ff3mHS5s5Jms+7BITAeh6Rgt+DyNbykEAvzt+MCHK9cP
xtsIZQlvRF7HIrpOlaRzOPp3sK2/MDZJ1RBE7wYItK3CUAmsHim/LVYKzZkRTij3
/9b4LP1yMMbziG+Yxt1o682EwJB5YIat6fmDG9uFeEVI5rWWN7WFubqs8gCjYy/p
FX+BjpOdgTRnX+1m9GIj0Jlc/HKMXryDfSZS07Zy4FbGEwSiI5SfKECub4mDhuE=
=C/uD
-----END PGP SIGNATURE-----
Merge tag 'kvm-4.16-1' of git://git.kernel.org/pub/scm/virt/kvm/kvm
Pull KVM updates from Radim Krčmář:
"ARM:
- icache invalidation optimizations, improving VM startup time
- support for forwarded level-triggered interrupts, improving
performance for timers and passthrough platform devices
- a small fix for power-management notifiers, and some cosmetic
changes
PPC:
- add MMIO emulation for vector loads and stores
- allow HPT guests to run on a radix host on POWER9 v2.2 CPUs without
requiring the complex thread synchronization of older CPU versions
- improve the handling of escalation interrupts with the XIVE
interrupt controller
- support decrement register migration
- various cleanups and bugfixes.
s390:
- Cornelia Huck passed maintainership to Janosch Frank
- exitless interrupts for emulated devices
- cleanup of cpuflag handling
- kvm_stat counter improvements
- VSIE improvements
- mm cleanup
x86:
- hypervisor part of SEV
- UMIP, RDPID, and MSR_SMI_COUNT emulation
- paravirtualized TLB shootdown using the new KVM_VCPU_PREEMPTED bit
- allow guests to see TOPOEXT, GFNI, VAES, VPCLMULQDQ, and more
AVX512 features
- show vcpu id in its anonymous inode name
- many fixes and cleanups
- per-VCPU MSR bitmaps (already merged through x86/pti branch)
- stable KVM clock when nesting on Hyper-V (merged through
x86/hyperv)"
* tag 'kvm-4.16-1' of git://git.kernel.org/pub/scm/virt/kvm/kvm: (197 commits)
KVM: PPC: Book3S: Add MMIO emulation for VMX instructions
KVM: PPC: Book3S HV: Branch inside feature section
KVM: PPC: Book3S HV: Make HPT resizing work on POWER9
KVM: PPC: Book3S HV: Fix handling of secondary HPTEG in HPT resizing code
KVM: PPC: Book3S PR: Fix broken select due to misspelling
KVM: x86: don't forget vcpu_put() in kvm_arch_vcpu_ioctl_set_sregs()
KVM: PPC: Book3S PR: Fix svcpu copying with preemption enabled
KVM: PPC: Book3S HV: Drop locks before reading guest memory
kvm: x86: remove efer_reload entry in kvm_vcpu_stat
KVM: x86: AMD Processor Topology Information
x86/kvm/vmx: do not use vm-exit instruction length for fast MMIO when running nested
kvm: embed vcpu id to dentry of vcpu anon inode
kvm: Map PFN-type memory regions as writable (if possible)
x86/kvm: Make it compile on 32bit and with HYPYERVISOR_GUEST=n
KVM: arm/arm64: Fixup userspace irqchip static key optimization
KVM: arm/arm64: Fix userspace_irqchip_in_use counting
KVM: arm/arm64: Fix incorrect timer_is_pending logic
MAINTAINERS: update KVM/s390 maintainers
MAINTAINERS: add Halil as additional vfio-ccw maintainer
MAINTAINERS: add David as a reviewer for KVM/s390
...
This commit is contained in:
commit
15303ba5d1
123 changed files with 6577 additions and 1416 deletions
|
|
@ -1,5 +1,6 @@
|
|||
config CRYPTO_DEV_CCP_DD
|
||||
tristate "Secure Processor device driver"
|
||||
depends on CPU_SUP_AMD || ARM64
|
||||
default m
|
||||
help
|
||||
Provides AMD Secure Processor device driver.
|
||||
|
|
@ -32,3 +33,14 @@ config CRYPTO_DEV_CCP_CRYPTO
|
|||
Support for using the cryptographic API with the AMD Cryptographic
|
||||
Coprocessor. This module supports offload of SHA and AES algorithms.
|
||||
If you choose 'M' here, this module will be called ccp_crypto.
|
||||
|
||||
config CRYPTO_DEV_SP_PSP
|
||||
bool "Platform Security Processor (PSP) device"
|
||||
default y
|
||||
depends on CRYPTO_DEV_CCP_DD && X86_64
|
||||
help
|
||||
Provide support for the AMD Platform Security Processor (PSP).
|
||||
The PSP is a dedicated processor that provides support for key
|
||||
management commands in Secure Encrypted Virtualization (SEV) mode,
|
||||
along with software-based Trusted Execution Environment (TEE) to
|
||||
enable third-party trusted applications.
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ ccp-$(CONFIG_CRYPTO_DEV_SP_CCP) += ccp-dev.o \
|
|||
ccp-dmaengine.o \
|
||||
ccp-debugfs.o
|
||||
ccp-$(CONFIG_PCI) += sp-pci.o
|
||||
ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += psp-dev.o
|
||||
|
||||
obj-$(CONFIG_CRYPTO_DEV_CCP_CRYPTO) += ccp-crypto.o
|
||||
ccp-crypto-objs := ccp-crypto-main.o \
|
||||
|
|
|
|||
805
drivers/crypto/ccp/psp-dev.c
Normal file
805
drivers/crypto/ccp/psp-dev.c
Normal file
|
|
@ -0,0 +1,805 @@
|
|||
/*
|
||||
* AMD Platform Security Processor (PSP) interface
|
||||
*
|
||||
* Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Author: Brijesh Singh <brijesh.singh@amd.com>
|
||||
*
|
||||
* 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/kthread.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/spinlock_types.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/ccp.h>
|
||||
|
||||
#include "sp-dev.h"
|
||||
#include "psp-dev.h"
|
||||
|
||||
#define DEVICE_NAME "sev"
|
||||
|
||||
static DEFINE_MUTEX(sev_cmd_mutex);
|
||||
static struct sev_misc_dev *misc_dev;
|
||||
static struct psp_device *psp_master;
|
||||
|
||||
static struct psp_device *psp_alloc_struct(struct sp_device *sp)
|
||||
{
|
||||
struct device *dev = sp->dev;
|
||||
struct psp_device *psp;
|
||||
|
||||
psp = devm_kzalloc(dev, sizeof(*psp), GFP_KERNEL);
|
||||
if (!psp)
|
||||
return NULL;
|
||||
|
||||
psp->dev = dev;
|
||||
psp->sp = sp;
|
||||
|
||||
snprintf(psp->name, sizeof(psp->name), "psp-%u", sp->ord);
|
||||
|
||||
return psp;
|
||||
}
|
||||
|
||||
static irqreturn_t psp_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct psp_device *psp = data;
|
||||
unsigned int status;
|
||||
int reg;
|
||||
|
||||
/* Read the interrupt status: */
|
||||
status = ioread32(psp->io_regs + PSP_P2CMSG_INTSTS);
|
||||
|
||||
/* Check if it is command completion: */
|
||||
if (!(status & BIT(PSP_CMD_COMPLETE_REG)))
|
||||
goto done;
|
||||
|
||||
/* Check if it is SEV command completion: */
|
||||
reg = ioread32(psp->io_regs + PSP_CMDRESP);
|
||||
if (reg & PSP_CMDRESP_RESP) {
|
||||
psp->sev_int_rcvd = 1;
|
||||
wake_up(&psp->sev_int_queue);
|
||||
}
|
||||
|
||||
done:
|
||||
/* Clear the interrupt status by writing the same value we read. */
|
||||
iowrite32(status, psp->io_regs + PSP_P2CMSG_INTSTS);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void sev_wait_cmd_ioc(struct psp_device *psp, unsigned int *reg)
|
||||
{
|
||||
psp->sev_int_rcvd = 0;
|
||||
|
||||
wait_event(psp->sev_int_queue, psp->sev_int_rcvd);
|
||||
*reg = ioread32(psp->io_regs + PSP_CMDRESP);
|
||||
}
|
||||
|
||||
static int sev_cmd_buffer_len(int cmd)
|
||||
{
|
||||
switch (cmd) {
|
||||
case SEV_CMD_INIT: return sizeof(struct sev_data_init);
|
||||
case SEV_CMD_PLATFORM_STATUS: return sizeof(struct sev_user_data_status);
|
||||
case SEV_CMD_PEK_CSR: return sizeof(struct sev_data_pek_csr);
|
||||
case SEV_CMD_PEK_CERT_IMPORT: return sizeof(struct sev_data_pek_cert_import);
|
||||
case SEV_CMD_PDH_CERT_EXPORT: return sizeof(struct sev_data_pdh_cert_export);
|
||||
case SEV_CMD_LAUNCH_START: return sizeof(struct sev_data_launch_start);
|
||||
case SEV_CMD_LAUNCH_UPDATE_DATA: return sizeof(struct sev_data_launch_update_data);
|
||||
case SEV_CMD_LAUNCH_UPDATE_VMSA: return sizeof(struct sev_data_launch_update_vmsa);
|
||||
case SEV_CMD_LAUNCH_FINISH: return sizeof(struct sev_data_launch_finish);
|
||||
case SEV_CMD_LAUNCH_MEASURE: return sizeof(struct sev_data_launch_measure);
|
||||
case SEV_CMD_ACTIVATE: return sizeof(struct sev_data_activate);
|
||||
case SEV_CMD_DEACTIVATE: return sizeof(struct sev_data_deactivate);
|
||||
case SEV_CMD_DECOMMISSION: return sizeof(struct sev_data_decommission);
|
||||
case SEV_CMD_GUEST_STATUS: return sizeof(struct sev_data_guest_status);
|
||||
case SEV_CMD_DBG_DECRYPT: return sizeof(struct sev_data_dbg);
|
||||
case SEV_CMD_DBG_ENCRYPT: return sizeof(struct sev_data_dbg);
|
||||
case SEV_CMD_SEND_START: return sizeof(struct sev_data_send_start);
|
||||
case SEV_CMD_SEND_UPDATE_DATA: return sizeof(struct sev_data_send_update_data);
|
||||
case SEV_CMD_SEND_UPDATE_VMSA: return sizeof(struct sev_data_send_update_vmsa);
|
||||
case SEV_CMD_SEND_FINISH: return sizeof(struct sev_data_send_finish);
|
||||
case SEV_CMD_RECEIVE_START: return sizeof(struct sev_data_receive_start);
|
||||
case SEV_CMD_RECEIVE_FINISH: return sizeof(struct sev_data_receive_finish);
|
||||
case SEV_CMD_RECEIVE_UPDATE_DATA: return sizeof(struct sev_data_receive_update_data);
|
||||
case SEV_CMD_RECEIVE_UPDATE_VMSA: return sizeof(struct sev_data_receive_update_vmsa);
|
||||
case SEV_CMD_LAUNCH_UPDATE_SECRET: return sizeof(struct sev_data_launch_secret);
|
||||
default: return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __sev_do_cmd_locked(int cmd, void *data, int *psp_ret)
|
||||
{
|
||||
struct psp_device *psp = psp_master;
|
||||
unsigned int phys_lsb, phys_msb;
|
||||
unsigned int reg, ret = 0;
|
||||
|
||||
if (!psp)
|
||||
return -ENODEV;
|
||||
|
||||
/* Get the physical address of the command buffer */
|
||||
phys_lsb = data ? lower_32_bits(__psp_pa(data)) : 0;
|
||||
phys_msb = data ? upper_32_bits(__psp_pa(data)) : 0;
|
||||
|
||||
dev_dbg(psp->dev, "sev command id %#x buffer 0x%08x%08x\n",
|
||||
cmd, phys_msb, phys_lsb);
|
||||
|
||||
print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data,
|
||||
sev_cmd_buffer_len(cmd), false);
|
||||
|
||||
iowrite32(phys_lsb, psp->io_regs + PSP_CMDBUFF_ADDR_LO);
|
||||
iowrite32(phys_msb, psp->io_regs + PSP_CMDBUFF_ADDR_HI);
|
||||
|
||||
reg = cmd;
|
||||
reg <<= PSP_CMDRESP_CMD_SHIFT;
|
||||
reg |= PSP_CMDRESP_IOC;
|
||||
iowrite32(reg, psp->io_regs + PSP_CMDRESP);
|
||||
|
||||
/* wait for command completion */
|
||||
sev_wait_cmd_ioc(psp, ®);
|
||||
|
||||
if (psp_ret)
|
||||
*psp_ret = reg & PSP_CMDRESP_ERR_MASK;
|
||||
|
||||
if (reg & PSP_CMDRESP_ERR_MASK) {
|
||||
dev_dbg(psp->dev, "sev command %#x failed (%#010x)\n",
|
||||
cmd, reg & PSP_CMDRESP_ERR_MASK);
|
||||
ret = -EIO;
|
||||
}
|
||||
|
||||
print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data,
|
||||
sev_cmd_buffer_len(cmd), false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sev_do_cmd(int cmd, void *data, int *psp_ret)
|
||||
{
|
||||
int rc;
|
||||
|
||||
mutex_lock(&sev_cmd_mutex);
|
||||
rc = __sev_do_cmd_locked(cmd, data, psp_ret);
|
||||
mutex_unlock(&sev_cmd_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __sev_platform_init_locked(int *error)
|
||||
{
|
||||
struct psp_device *psp = psp_master;
|
||||
int rc = 0;
|
||||
|
||||
if (!psp)
|
||||
return -ENODEV;
|
||||
|
||||
if (psp->sev_state == SEV_STATE_INIT)
|
||||
return 0;
|
||||
|
||||
rc = __sev_do_cmd_locked(SEV_CMD_INIT, &psp->init_cmd_buf, error);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
psp->sev_state = SEV_STATE_INIT;
|
||||
dev_dbg(psp->dev, "SEV firmware initialized\n");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int sev_platform_init(int *error)
|
||||
{
|
||||
int rc;
|
||||
|
||||
mutex_lock(&sev_cmd_mutex);
|
||||
rc = __sev_platform_init_locked(error);
|
||||
mutex_unlock(&sev_cmd_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sev_platform_init);
|
||||
|
||||
static int __sev_platform_shutdown_locked(int *error)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = __sev_do_cmd_locked(SEV_CMD_SHUTDOWN, 0, error);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
psp_master->sev_state = SEV_STATE_UNINIT;
|
||||
dev_dbg(psp_master->dev, "SEV firmware shutdown\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sev_platform_shutdown(int *error)
|
||||
{
|
||||
int rc;
|
||||
|
||||
mutex_lock(&sev_cmd_mutex);
|
||||
rc = __sev_platform_shutdown_locked(NULL);
|
||||
mutex_unlock(&sev_cmd_mutex);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sev_get_platform_state(int *state, int *error)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS,
|
||||
&psp_master->status_cmd_buf, error);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
*state = psp_master->status_cmd_buf.state;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int sev_ioctl_do_reset(struct sev_issue_cmd *argp)
|
||||
{
|
||||
int state, rc;
|
||||
|
||||
/*
|
||||
* The SEV spec requires that FACTORY_RESET must be issued in
|
||||
* UNINIT state. Before we go further lets check if any guest is
|
||||
* active.
|
||||
*
|
||||
* If FW is in WORKING state then deny the request otherwise issue
|
||||
* SHUTDOWN command do INIT -> UNINIT before issuing the FACTORY_RESET.
|
||||
*
|
||||
*/
|
||||
rc = sev_get_platform_state(&state, &argp->error);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (state == SEV_STATE_WORKING)
|
||||
return -EBUSY;
|
||||
|
||||
if (state == SEV_STATE_INIT) {
|
||||
rc = __sev_platform_shutdown_locked(&argp->error);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return __sev_do_cmd_locked(SEV_CMD_FACTORY_RESET, 0, &argp->error);
|
||||
}
|
||||
|
||||
static int sev_ioctl_do_platform_status(struct sev_issue_cmd *argp)
|
||||
{
|
||||
struct sev_user_data_status *data = &psp_master->status_cmd_buf;
|
||||
int ret;
|
||||
|
||||
ret = __sev_do_cmd_locked(SEV_CMD_PLATFORM_STATUS, data, &argp->error);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (copy_to_user((void __user *)argp->data, data, sizeof(*data)))
|
||||
ret = -EFAULT;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sev_ioctl_do_pek_pdh_gen(int cmd, struct sev_issue_cmd *argp)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (psp_master->sev_state == SEV_STATE_UNINIT) {
|
||||
rc = __sev_platform_init_locked(&argp->error);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return __sev_do_cmd_locked(cmd, 0, &argp->error);
|
||||
}
|
||||
|
||||
static int sev_ioctl_do_pek_csr(struct sev_issue_cmd *argp)
|
||||
{
|
||||
struct sev_user_data_pek_csr input;
|
||||
struct sev_data_pek_csr *data;
|
||||
void *blob = NULL;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&input, (void __user *)argp->data, sizeof(input)))
|
||||
return -EFAULT;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* userspace wants to query CSR length */
|
||||
if (!input.address || !input.length)
|
||||
goto cmd;
|
||||
|
||||
/* allocate a physically contiguous buffer to store the CSR blob */
|
||||
if (!access_ok(VERIFY_WRITE, input.address, input.length) ||
|
||||
input.length > SEV_FW_BLOB_MAX_SIZE) {
|
||||
ret = -EFAULT;
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
blob = kmalloc(input.length, GFP_KERNEL);
|
||||
if (!blob) {
|
||||
ret = -ENOMEM;
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
data->address = __psp_pa(blob);
|
||||
data->len = input.length;
|
||||
|
||||
cmd:
|
||||
if (psp_master->sev_state == SEV_STATE_UNINIT) {
|
||||
ret = __sev_platform_init_locked(&argp->error);
|
||||
if (ret)
|
||||
goto e_free_blob;
|
||||
}
|
||||
|
||||
ret = __sev_do_cmd_locked(SEV_CMD_PEK_CSR, data, &argp->error);
|
||||
|
||||
/* If we query the CSR length, FW responded with expected data. */
|
||||
input.length = data->len;
|
||||
|
||||
if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) {
|
||||
ret = -EFAULT;
|
||||
goto e_free_blob;
|
||||
}
|
||||
|
||||
if (blob) {
|
||||
if (copy_to_user((void __user *)input.address, blob, input.length))
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
e_free_blob:
|
||||
kfree(blob);
|
||||
e_free:
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void *psp_copy_user_blob(u64 __user uaddr, u32 len)
|
||||
{
|
||||
void *data;
|
||||
|
||||
if (!uaddr || !len)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
/* verify that blob length does not exceed our limit */
|
||||
if (len > SEV_FW_BLOB_MAX_SIZE)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
data = kmalloc(len, GFP_KERNEL);
|
||||
if (!data)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
if (copy_from_user(data, (void __user *)(uintptr_t)uaddr, len))
|
||||
goto e_free;
|
||||
|
||||
return data;
|
||||
|
||||
e_free:
|
||||
kfree(data);
|
||||
return ERR_PTR(-EFAULT);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(psp_copy_user_blob);
|
||||
|
||||
static int sev_ioctl_do_pek_import(struct sev_issue_cmd *argp)
|
||||
{
|
||||
struct sev_user_data_pek_cert_import input;
|
||||
struct sev_data_pek_cert_import *data;
|
||||
void *pek_blob, *oca_blob;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&input, (void __user *)argp->data, sizeof(input)))
|
||||
return -EFAULT;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* copy PEK certificate blobs from userspace */
|
||||
pek_blob = psp_copy_user_blob(input.pek_cert_address, input.pek_cert_len);
|
||||
if (IS_ERR(pek_blob)) {
|
||||
ret = PTR_ERR(pek_blob);
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
data->pek_cert_address = __psp_pa(pek_blob);
|
||||
data->pek_cert_len = input.pek_cert_len;
|
||||
|
||||
/* copy PEK certificate blobs from userspace */
|
||||
oca_blob = psp_copy_user_blob(input.oca_cert_address, input.oca_cert_len);
|
||||
if (IS_ERR(oca_blob)) {
|
||||
ret = PTR_ERR(oca_blob);
|
||||
goto e_free_pek;
|
||||
}
|
||||
|
||||
data->oca_cert_address = __psp_pa(oca_blob);
|
||||
data->oca_cert_len = input.oca_cert_len;
|
||||
|
||||
/* If platform is not in INIT state then transition it to INIT */
|
||||
if (psp_master->sev_state != SEV_STATE_INIT) {
|
||||
ret = __sev_platform_init_locked(&argp->error);
|
||||
if (ret)
|
||||
goto e_free_oca;
|
||||
}
|
||||
|
||||
ret = __sev_do_cmd_locked(SEV_CMD_PEK_CERT_IMPORT, data, &argp->error);
|
||||
|
||||
e_free_oca:
|
||||
kfree(oca_blob);
|
||||
e_free_pek:
|
||||
kfree(pek_blob);
|
||||
e_free:
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sev_ioctl_do_pdh_export(struct sev_issue_cmd *argp)
|
||||
{
|
||||
struct sev_user_data_pdh_cert_export input;
|
||||
void *pdh_blob = NULL, *cert_blob = NULL;
|
||||
struct sev_data_pdh_cert_export *data;
|
||||
int ret;
|
||||
|
||||
if (copy_from_user(&input, (void __user *)argp->data, sizeof(input)))
|
||||
return -EFAULT;
|
||||
|
||||
data = kzalloc(sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Userspace wants to query the certificate length. */
|
||||
if (!input.pdh_cert_address ||
|
||||
!input.pdh_cert_len ||
|
||||
!input.cert_chain_address)
|
||||
goto cmd;
|
||||
|
||||
/* Allocate a physically contiguous buffer to store the PDH blob. */
|
||||
if ((input.pdh_cert_len > SEV_FW_BLOB_MAX_SIZE) ||
|
||||
!access_ok(VERIFY_WRITE, input.pdh_cert_address, input.pdh_cert_len)) {
|
||||
ret = -EFAULT;
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
/* Allocate a physically contiguous buffer to store the cert chain blob. */
|
||||
if ((input.cert_chain_len > SEV_FW_BLOB_MAX_SIZE) ||
|
||||
!access_ok(VERIFY_WRITE, input.cert_chain_address, input.cert_chain_len)) {
|
||||
ret = -EFAULT;
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
pdh_blob = kmalloc(input.pdh_cert_len, GFP_KERNEL);
|
||||
if (!pdh_blob) {
|
||||
ret = -ENOMEM;
|
||||
goto e_free;
|
||||
}
|
||||
|
||||
data->pdh_cert_address = __psp_pa(pdh_blob);
|
||||
data->pdh_cert_len = input.pdh_cert_len;
|
||||
|
||||
cert_blob = kmalloc(input.cert_chain_len, GFP_KERNEL);
|
||||
if (!cert_blob) {
|
||||
ret = -ENOMEM;
|
||||
goto e_free_pdh;
|
||||
}
|
||||
|
||||
data->cert_chain_address = __psp_pa(cert_blob);
|
||||
data->cert_chain_len = input.cert_chain_len;
|
||||
|
||||
cmd:
|
||||
/* If platform is not in INIT state then transition it to INIT. */
|
||||
if (psp_master->sev_state != SEV_STATE_INIT) {
|
||||
ret = __sev_platform_init_locked(&argp->error);
|
||||
if (ret)
|
||||
goto e_free_cert;
|
||||
}
|
||||
|
||||
ret = __sev_do_cmd_locked(SEV_CMD_PDH_CERT_EXPORT, data, &argp->error);
|
||||
|
||||
/* If we query the length, FW responded with expected data. */
|
||||
input.cert_chain_len = data->cert_chain_len;
|
||||
input.pdh_cert_len = data->pdh_cert_len;
|
||||
|
||||
if (copy_to_user((void __user *)argp->data, &input, sizeof(input))) {
|
||||
ret = -EFAULT;
|
||||
goto e_free_cert;
|
||||
}
|
||||
|
||||
if (pdh_blob) {
|
||||
if (copy_to_user((void __user *)input.pdh_cert_address,
|
||||
pdh_blob, input.pdh_cert_len)) {
|
||||
ret = -EFAULT;
|
||||
goto e_free_cert;
|
||||
}
|
||||
}
|
||||
|
||||
if (cert_blob) {
|
||||
if (copy_to_user((void __user *)input.cert_chain_address,
|
||||
cert_blob, input.cert_chain_len))
|
||||
ret = -EFAULT;
|
||||
}
|
||||
|
||||
e_free_cert:
|
||||
kfree(cert_blob);
|
||||
e_free_pdh:
|
||||
kfree(pdh_blob);
|
||||
e_free:
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
struct sev_issue_cmd input;
|
||||
int ret = -EFAULT;
|
||||
|
||||
if (!psp_master)
|
||||
return -ENODEV;
|
||||
|
||||
if (ioctl != SEV_ISSUE_CMD)
|
||||
return -EINVAL;
|
||||
|
||||
if (copy_from_user(&input, argp, sizeof(struct sev_issue_cmd)))
|
||||
return -EFAULT;
|
||||
|
||||
if (input.cmd > SEV_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&sev_cmd_mutex);
|
||||
|
||||
switch (input.cmd) {
|
||||
|
||||
case SEV_FACTORY_RESET:
|
||||
ret = sev_ioctl_do_reset(&input);
|
||||
break;
|
||||
case SEV_PLATFORM_STATUS:
|
||||
ret = sev_ioctl_do_platform_status(&input);
|
||||
break;
|
||||
case SEV_PEK_GEN:
|
||||
ret = sev_ioctl_do_pek_pdh_gen(SEV_CMD_PEK_GEN, &input);
|
||||
break;
|
||||
case SEV_PDH_GEN:
|
||||
ret = sev_ioctl_do_pek_pdh_gen(SEV_CMD_PDH_GEN, &input);
|
||||
break;
|
||||
case SEV_PEK_CSR:
|
||||
ret = sev_ioctl_do_pek_csr(&input);
|
||||
break;
|
||||
case SEV_PEK_CERT_IMPORT:
|
||||
ret = sev_ioctl_do_pek_import(&input);
|
||||
break;
|
||||
case SEV_PDH_CERT_EXPORT:
|
||||
ret = sev_ioctl_do_pdh_export(&input);
|
||||
break;
|
||||
default:
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (copy_to_user(argp, &input, sizeof(struct sev_issue_cmd)))
|
||||
ret = -EFAULT;
|
||||
out:
|
||||
mutex_unlock(&sev_cmd_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations sev_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.unlocked_ioctl = sev_ioctl,
|
||||
};
|
||||
|
||||
int sev_platform_status(struct sev_user_data_status *data, int *error)
|
||||
{
|
||||
return sev_do_cmd(SEV_CMD_PLATFORM_STATUS, data, error);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sev_platform_status);
|
||||
|
||||
int sev_guest_deactivate(struct sev_data_deactivate *data, int *error)
|
||||
{
|
||||
return sev_do_cmd(SEV_CMD_DEACTIVATE, data, error);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sev_guest_deactivate);
|
||||
|
||||
int sev_guest_activate(struct sev_data_activate *data, int *error)
|
||||
{
|
||||
return sev_do_cmd(SEV_CMD_ACTIVATE, data, error);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sev_guest_activate);
|
||||
|
||||
int sev_guest_decommission(struct sev_data_decommission *data, int *error)
|
||||
{
|
||||
return sev_do_cmd(SEV_CMD_DECOMMISSION, data, error);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sev_guest_decommission);
|
||||
|
||||
int sev_guest_df_flush(int *error)
|
||||
{
|
||||
return sev_do_cmd(SEV_CMD_DF_FLUSH, 0, error);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sev_guest_df_flush);
|
||||
|
||||
static void sev_exit(struct kref *ref)
|
||||
{
|
||||
struct sev_misc_dev *misc_dev = container_of(ref, struct sev_misc_dev, refcount);
|
||||
|
||||
misc_deregister(&misc_dev->misc);
|
||||
}
|
||||
|
||||
static int sev_misc_init(struct psp_device *psp)
|
||||
{
|
||||
struct device *dev = psp->dev;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* SEV feature support can be detected on multiple devices but the SEV
|
||||
* FW commands must be issued on the master. During probe, we do not
|
||||
* know the master hence we create /dev/sev on the first device probe.
|
||||
* sev_do_cmd() finds the right master device to which to issue the
|
||||
* command to the firmware.
|
||||
*/
|
||||
if (!misc_dev) {
|
||||
struct miscdevice *misc;
|
||||
|
||||
misc_dev = devm_kzalloc(dev, sizeof(*misc_dev), GFP_KERNEL);
|
||||
if (!misc_dev)
|
||||
return -ENOMEM;
|
||||
|
||||
misc = &misc_dev->misc;
|
||||
misc->minor = MISC_DYNAMIC_MINOR;
|
||||
misc->name = DEVICE_NAME;
|
||||
misc->fops = &sev_fops;
|
||||
|
||||
ret = misc_register(misc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kref_init(&misc_dev->refcount);
|
||||
} else {
|
||||
kref_get(&misc_dev->refcount);
|
||||
}
|
||||
|
||||
init_waitqueue_head(&psp->sev_int_queue);
|
||||
psp->sev_misc = misc_dev;
|
||||
dev_dbg(dev, "registered SEV device\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sev_init(struct psp_device *psp)
|
||||
{
|
||||
/* Check if device supports SEV feature */
|
||||
if (!(ioread32(psp->io_regs + PSP_FEATURE_REG) & 1)) {
|
||||
dev_dbg(psp->dev, "device does not support SEV\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return sev_misc_init(psp);
|
||||
}
|
||||
|
||||
int psp_dev_init(struct sp_device *sp)
|
||||
{
|
||||
struct device *dev = sp->dev;
|
||||
struct psp_device *psp;
|
||||
int ret;
|
||||
|
||||
ret = -ENOMEM;
|
||||
psp = psp_alloc_struct(sp);
|
||||
if (!psp)
|
||||
goto e_err;
|
||||
|
||||
sp->psp_data = psp;
|
||||
|
||||
psp->vdata = (struct psp_vdata *)sp->dev_vdata->psp_vdata;
|
||||
if (!psp->vdata) {
|
||||
ret = -ENODEV;
|
||||
dev_err(dev, "missing driver data\n");
|
||||
goto e_err;
|
||||
}
|
||||
|
||||
psp->io_regs = sp->io_map + psp->vdata->offset;
|
||||
|
||||
/* Disable and clear interrupts until ready */
|
||||
iowrite32(0, psp->io_regs + PSP_P2CMSG_INTEN);
|
||||
iowrite32(-1, psp->io_regs + PSP_P2CMSG_INTSTS);
|
||||
|
||||
/* Request an irq */
|
||||
ret = sp_request_psp_irq(psp->sp, psp_irq_handler, psp->name, psp);
|
||||
if (ret) {
|
||||
dev_err(dev, "psp: unable to allocate an IRQ\n");
|
||||
goto e_err;
|
||||
}
|
||||
|
||||
ret = sev_init(psp);
|
||||
if (ret)
|
||||
goto e_irq;
|
||||
|
||||
if (sp->set_psp_master_device)
|
||||
sp->set_psp_master_device(sp);
|
||||
|
||||
/* Enable interrupt */
|
||||
iowrite32(-1, psp->io_regs + PSP_P2CMSG_INTEN);
|
||||
|
||||
return 0;
|
||||
|
||||
e_irq:
|
||||
sp_free_psp_irq(psp->sp, psp);
|
||||
e_err:
|
||||
sp->psp_data = NULL;
|
||||
|
||||
dev_notice(dev, "psp initialization failed\n");
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void psp_dev_destroy(struct sp_device *sp)
|
||||
{
|
||||
struct psp_device *psp = sp->psp_data;
|
||||
|
||||
if (psp->sev_misc)
|
||||
kref_put(&misc_dev->refcount, sev_exit);
|
||||
|
||||
sp_free_psp_irq(sp, psp);
|
||||
}
|
||||
|
||||
int sev_issue_cmd_external_user(struct file *filep, unsigned int cmd,
|
||||
void *data, int *error)
|
||||
{
|
||||
if (!filep || filep->f_op != &sev_fops)
|
||||
return -EBADF;
|
||||
|
||||
return sev_do_cmd(cmd, data, error);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user);
|
||||
|
||||
void psp_pci_init(void)
|
||||
{
|
||||
struct sev_user_data_status *status;
|
||||
struct sp_device *sp;
|
||||
int error, rc;
|
||||
|
||||
sp = sp_get_psp_master_device();
|
||||
if (!sp)
|
||||
return;
|
||||
|
||||
psp_master = sp->psp_data;
|
||||
|
||||
/* Initialize the platform */
|
||||
rc = sev_platform_init(&error);
|
||||
if (rc) {
|
||||
dev_err(sp->dev, "SEV: failed to INIT error %#x\n", error);
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* Display SEV firmware version */
|
||||
status = &psp_master->status_cmd_buf;
|
||||
rc = sev_platform_status(status, &error);
|
||||
if (rc) {
|
||||
dev_err(sp->dev, "SEV: failed to get status error %#x\n", error);
|
||||
goto err;
|
||||
}
|
||||
|
||||
dev_info(sp->dev, "SEV API:%d.%d build:%d\n", status->api_major,
|
||||
status->api_minor, status->build);
|
||||
return;
|
||||
|
||||
err:
|
||||
psp_master = NULL;
|
||||
}
|
||||
|
||||
void psp_pci_exit(void)
|
||||
{
|
||||
if (!psp_master)
|
||||
return;
|
||||
|
||||
sev_platform_shutdown(NULL);
|
||||
}
|
||||
83
drivers/crypto/ccp/psp-dev.h
Normal file
83
drivers/crypto/ccp/psp-dev.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* AMD Platform Security Processor (PSP) interface driver
|
||||
*
|
||||
* Copyright (C) 2017 Advanced Micro Devices, Inc.
|
||||
*
|
||||
* Author: Brijesh Singh <brijesh.singh@amd.com>
|
||||
*
|
||||
* 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 __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/bitops.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/psp-sev.h>
|
||||
#include <linux/miscdevice.h>
|
||||
|
||||
#include "sp-dev.h"
|
||||
|
||||
#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_FEATURE_REG PSP_C2PMSG(63)
|
||||
|
||||
#define PSP_P2CMSG(_num) ((_num) << 2)
|
||||
#define PSP_CMD_COMPLETE_REG 1
|
||||
#define PSP_CMD_COMPLETE PSP_P2CMSG(PSP_CMD_COMPLETE_REG)
|
||||
|
||||
#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_CMDRESP_CMD_SHIFT 16
|
||||
#define PSP_CMDRESP_IOC BIT(0)
|
||||
#define PSP_CMDRESP_RESP BIT(31)
|
||||
#define PSP_CMDRESP_ERR_MASK 0xffff
|
||||
|
||||
#define MAX_PSP_NAME_LEN 16
|
||||
|
||||
struct sev_misc_dev {
|
||||
struct kref refcount;
|
||||
struct miscdevice misc;
|
||||
};
|
||||
|
||||
struct psp_device {
|
||||
struct list_head entry;
|
||||
|
||||
struct psp_vdata *vdata;
|
||||
char name[MAX_PSP_NAME_LEN];
|
||||
|
||||
struct device *dev;
|
||||
struct sp_device *sp;
|
||||
|
||||
void __iomem *io_regs;
|
||||
|
||||
int sev_state;
|
||||
unsigned int sev_int_rcvd;
|
||||
wait_queue_head_t sev_int_queue;
|
||||
struct sev_misc_dev *sev_misc;
|
||||
struct sev_user_data_status status_cmd_buf;
|
||||
struct sev_data_init init_cmd_buf;
|
||||
};
|
||||
|
||||
#endif /* __PSP_DEV_H */
|
||||
|
|
@ -198,6 +198,8 @@ int sp_init(struct sp_device *sp)
|
|||
if (sp->dev_vdata->ccp_vdata)
|
||||
ccp_dev_init(sp);
|
||||
|
||||
if (sp->dev_vdata->psp_vdata)
|
||||
psp_dev_init(sp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -206,6 +208,9 @@ void sp_destroy(struct sp_device *sp)
|
|||
if (sp->dev_vdata->ccp_vdata)
|
||||
ccp_dev_destroy(sp);
|
||||
|
||||
if (sp->dev_vdata->psp_vdata)
|
||||
psp_dev_destroy(sp);
|
||||
|
||||
sp_del_device(sp);
|
||||
}
|
||||
|
||||
|
|
@ -237,6 +242,27 @@ int sp_resume(struct sp_device *sp)
|
|||
}
|
||||
#endif
|
||||
|
||||
struct sp_device *sp_get_psp_master_device(void)
|
||||
{
|
||||
struct sp_device *i, *ret = NULL;
|
||||
unsigned long flags;
|
||||
|
||||
write_lock_irqsave(&sp_unit_lock, flags);
|
||||
if (list_empty(&sp_units))
|
||||
goto unlock;
|
||||
|
||||
list_for_each_entry(i, &sp_units, entry) {
|
||||
if (i->psp_data)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i->get_psp_master_device)
|
||||
ret = i->get_psp_master_device();
|
||||
unlock:
|
||||
write_unlock_irqrestore(&sp_unit_lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __init sp_mod_init(void)
|
||||
{
|
||||
#ifdef CONFIG_X86
|
||||
|
|
@ -246,6 +272,10 @@ static int __init sp_mod_init(void)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
#ifdef CONFIG_CRYPTO_DEV_SP_PSP
|
||||
psp_pci_init();
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
|
|
@ -265,6 +295,11 @@ static int __init sp_mod_init(void)
|
|||
static void __exit sp_mod_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_X86
|
||||
|
||||
#ifdef CONFIG_CRYPTO_DEV_SP_PSP
|
||||
psp_pci_exit();
|
||||
#endif
|
||||
|
||||
sp_pci_exit();
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -42,12 +42,17 @@ struct ccp_vdata {
|
|||
const unsigned int offset;
|
||||
const unsigned int rsamax;
|
||||
};
|
||||
|
||||
struct psp_vdata {
|
||||
const unsigned int offset;
|
||||
};
|
||||
|
||||
/* Structure to hold SP device data */
|
||||
struct sp_dev_vdata {
|
||||
const unsigned int bar;
|
||||
|
||||
const struct ccp_vdata *ccp_vdata;
|
||||
void *psp_vdata;
|
||||
const struct psp_vdata *psp_vdata;
|
||||
};
|
||||
|
||||
struct sp_device {
|
||||
|
|
@ -68,6 +73,10 @@ struct sp_device {
|
|||
/* DMA caching attribute support */
|
||||
unsigned int axcache;
|
||||
|
||||
/* get and set master device */
|
||||
struct sp_device*(*get_psp_master_device)(void);
|
||||
void (*set_psp_master_device)(struct sp_device *);
|
||||
|
||||
bool irq_registered;
|
||||
bool use_tasklet;
|
||||
|
||||
|
|
@ -103,6 +112,7 @@ void sp_free_ccp_irq(struct sp_device *sp, void *data);
|
|||
int sp_request_psp_irq(struct sp_device *sp, irq_handler_t handler,
|
||||
const char *name, void *data);
|
||||
void sp_free_psp_irq(struct sp_device *sp, void *data);
|
||||
struct sp_device *sp_get_psp_master_device(void);
|
||||
|
||||
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
|
||||
|
||||
|
|
@ -130,4 +140,20 @@ static inline int ccp_dev_resume(struct sp_device *sp)
|
|||
}
|
||||
#endif /* CONFIG_CRYPTO_DEV_SP_CCP */
|
||||
|
||||
#ifdef CONFIG_CRYPTO_DEV_SP_PSP
|
||||
|
||||
int psp_dev_init(struct sp_device *sp);
|
||||
void psp_pci_init(void);
|
||||
void psp_dev_destroy(struct sp_device *sp);
|
||||
void psp_pci_exit(void);
|
||||
|
||||
#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
|
||||
|
||||
static inline int psp_dev_init(struct sp_device *sp) { return 0; }
|
||||
static inline void psp_pci_init(void) { }
|
||||
static inline void psp_dev_destroy(struct sp_device *sp) { }
|
||||
static inline void psp_pci_exit(void) { }
|
||||
|
||||
#endif /* CONFIG_CRYPTO_DEV_SP_PSP */
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/ccp.h>
|
||||
|
||||
#include "ccp-dev.h"
|
||||
#include "psp-dev.h"
|
||||
|
||||
#define MSIX_VECTORS 2
|
||||
|
||||
|
|
@ -32,6 +33,7 @@ struct sp_pci {
|
|||
int msix_count;
|
||||
struct msix_entry msix_entry[MSIX_VECTORS];
|
||||
};
|
||||
static struct sp_device *sp_dev_master;
|
||||
|
||||
static int sp_get_msix_irqs(struct sp_device *sp)
|
||||
{
|
||||
|
|
@ -108,6 +110,45 @@ static void sp_free_irqs(struct sp_device *sp)
|
|||
sp->psp_irq = 0;
|
||||
}
|
||||
|
||||
static bool sp_pci_is_master(struct sp_device *sp)
|
||||
{
|
||||
struct device *dev_cur, *dev_new;
|
||||
struct pci_dev *pdev_cur, *pdev_new;
|
||||
|
||||
dev_new = sp->dev;
|
||||
dev_cur = sp_dev_master->dev;
|
||||
|
||||
pdev_new = to_pci_dev(dev_new);
|
||||
pdev_cur = to_pci_dev(dev_cur);
|
||||
|
||||
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 void psp_set_master(struct sp_device *sp)
|
||||
{
|
||||
if (!sp_dev_master) {
|
||||
sp_dev_master = sp;
|
||||
return;
|
||||
}
|
||||
|
||||
if (sp_pci_is_master(sp))
|
||||
sp_dev_master = sp;
|
||||
}
|
||||
|
||||
static struct sp_device *psp_get_master(void)
|
||||
{
|
||||
return sp_dev_master;
|
||||
}
|
||||
|
||||
static int sp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct sp_device *sp;
|
||||
|
|
@ -166,6 +207,8 @@ static int sp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
goto e_err;
|
||||
|
||||
pci_set_master(pdev);
|
||||
sp->set_psp_master_device = psp_set_master;
|
||||
sp->get_psp_master_device = psp_get_master;
|
||||
|
||||
ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(48));
|
||||
if (ret) {
|
||||
|
|
@ -225,6 +268,12 @@ static int sp_pci_resume(struct pci_dev *pdev)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_CRYPTO_DEV_SP_PSP
|
||||
static const struct psp_vdata psp_entry = {
|
||||
.offset = 0x10500,
|
||||
};
|
||||
#endif
|
||||
|
||||
static const struct sp_dev_vdata dev_vdata[] = {
|
||||
{
|
||||
.bar = 2,
|
||||
|
|
@ -236,6 +285,9 @@ static const struct sp_dev_vdata dev_vdata[] = {
|
|||
.bar = 2,
|
||||
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
|
||||
.ccp_vdata = &ccpv5a,
|
||||
#endif
|
||||
#ifdef CONFIG_CRYPTO_DEV_SP_PSP
|
||||
.psp_vdata = &psp_entry
|
||||
#endif
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue