This part of Secure Encryted Virtualization (SEV) patch series focuses on KVM
changes required to create and manage SEV guests.
SEV is an extension to the AMD-V architecture which supports running encrypted
virtual machine (VMs) under the control of a hypervisor. Encrypted VMs have their
pages (code and data) secured such that only the guest itself has access to
unencrypted version. Each encrypted VM is associated with a unique encryption key;
if its data is accessed to a different entity using a different key the encrypted
guest's data will be incorrectly decrypted, leading to unintelligible data.
This security model ensures that hypervisor will no longer able to inspect or
alter any guest code or data.
The key management of this feature is handled by a separate processor known as
the AMD Secure Processor (AMD-SP) which is present on AMD SOCs. The SEV Key
Management Specification (see below) provides a set of commands which can be
used by hypervisor to load virtual machine keys through the AMD-SP driver.
The patch series adds a new ioctl in KVM driver (KVM_MEMORY_ENCRYPTION_OP). The
ioctl will be used by qemu to issue SEV guest-specific commands defined in Key
Management Specification.
The following links provide additional details:
AMD Memory Encryption whitepaper:
http://amd-dev.wpengine.netdna-cdn.com/wordpress/media/2013/12/AMD_Memory_Encryption_Whitepaper_v7-Public.pdf
AMD64 Architecture Programmer's Manual:
http://support.amd.com/TechDocs/24593.pdf
SME is section 7.10
SEV is section 15.34
Secure Encrypted Virutualization Key Management:
http://support.amd.com/TechDocs/55766_SEV-KM API_Specification.pdf
KVM Forum Presentation:
http://www.linux-kvm.org/images/7/74/02x08A-Thomas_Lendacky-AMDs_Virtualizatoin_Memory_Encryption_Technology.pdf
SEV Guest BIOS support:
SEV support has been interated into EDKII/OVMF BIOS
https://github.com/tianocore/edk2
RFC part1:
http://marc.info/?l=kvm&m=150092330804060&w=2
---
This RFC is based on tip/master commit : 22db3de (Merge branch 'x86/mm').
Complete git tree is available: https://github.com/codomania/tip/tree/sev-rfc-3
TODO:
* Add SEV guest migration command support
Cc: Herbert Xu <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: Gary Hook <[email protected]>
Cc: [email protected]
Changes since v2:
* Add KVM_MEMORY_ENCRYPT_REGISTER/UNREGISTER_RAM ioct to register encrypted
memory ranges (recommend by Paolo)
* Extend kvm_x86_ops to provide new memory_encryption_enabled ops
* Enhance DEBUG DECRYPT/ENCRYPT commands to work with more than one page (recommended by Paolo)
* Optimize LAUNCH_UPDATE command to reduce the number of calls to AMD-SP driver
* Changes to address v2 feedbacks
Brijesh Singh (24):
Documentation/virtual/kvm: Add AMD Secure Encrypted Virtualization
(SEV)
crypto: ccp: Add Platform Security Processor (PSP) device support
crypto: ccp: Add Secure Encrypted Virtualization (SEV) device support
KVM: SVM: Prepare to reserve asid for SEV guest
KVM: SVM: Reserve ASID range for SEV guest
KVM: X86: Extend CPUID range to include new leaf
KVM: Introduce KVM_MEMORY_ENCRYPT_OP ioctl
KVM: Introduce KVM_MEMORY_ENCRYPT_REGISTER/UNREGISTER_RAM ioctl
KVM: X86: Extend struct kvm_arch to include SEV information
KVM: Define SEV key management command id
KVM: SVM: Add KVM_SEV_INIT command
KVM: SVM: VMRUN should use assosiated ASID when SEV is enabled
KVM: SVM: Add support for SEV LAUNCH_START command
KVM: SVM: Add support for SEV LAUNCH_UPDATE_DATA command
KVM: SVM: Add support for SEV LAUNCH_MEASURE command
KVM: SVM: Add support for SEV LAUNCH_FINISH command
KVM: svm: Add support for SEV GUEST_STATUS command
KVM: SVM: Add support for SEV DEBUG_DECRYPT command
KVM: SVM: Add support for SEV DEBUG_ENCRYPT command
KVM: SVM: Pin guest memory when SEV is active
KVM: X86: Add memory encryption enabled ops
KVM: SVM: Clear C-bit from the page fault address
KVM: SVM: Do not install #UD intercept when SEV is enabled
KVM: X86: Restart the guest when insn_len is zero and SEV is enabled
Tom Lendacky (2):
KVM: SVM: Prepare for new bit definition in nested_ctl
KVM: SVM: Add SEV feature definitions to KVM
.../virtual/kvm/amd-memory-encryption.txt | 328 ++++++
arch/x86/include/asm/kvm_host.h | 17 +
arch/x86/include/asm/svm.h | 3 +
arch/x86/kvm/cpuid.c | 2 +-
arch/x86/kvm/mmu.c | 17 +
arch/x86/kvm/svm.c | 1221 +++++++++++++++++++-
arch/x86/kvm/x86.c | 48 +
drivers/crypto/ccp/Kconfig | 19 +
drivers/crypto/ccp/Makefile | 2 +
drivers/crypto/ccp/psp-dev.c | 230 ++++
drivers/crypto/ccp/psp-dev.h | 109 ++
drivers/crypto/ccp/sev-dev.c | 416 +++++++
drivers/crypto/ccp/sev-dev.h | 67 ++
drivers/crypto/ccp/sev-ops.c | 457 ++++++++
drivers/crypto/ccp/sp-dev.c | 43 +
drivers/crypto/ccp/sp-dev.h | 41 +-
drivers/crypto/ccp/sp-pci.c | 46 +
include/linux/psp-sev.h | 683 +++++++++++
include/uapi/linux/kvm.h | 159 +++
include/uapi/linux/psp-sev.h | 110 ++
20 files changed, 4010 insertions(+), 8 deletions(-)
create mode 100644 Documentation/virtual/kvm/amd-memory-encryption.txt
create mode 100644 drivers/crypto/ccp/psp-dev.c
create mode 100644 drivers/crypto/ccp/psp-dev.h
create mode 100644 drivers/crypto/ccp/sev-dev.c
create mode 100644 drivers/crypto/ccp/sev-dev.h
create mode 100644 drivers/crypto/ccp/sev-ops.c
create mode 100644 include/linux/psp-sev.h
create mode 100644 include/uapi/linux/psp-sev.h
--
Brijesh Singh
2.9.4
Create a Documentation entry to describe the AMD Secure Encrypted
Virtualization (SEV) feature.
Signed-off-by: Brijesh Singh <[email protected]>
---
.../virtual/kvm/amd-memory-encryption.txt | 328 +++++++++++++++++++++
1 file changed, 328 insertions(+)
create mode 100644 Documentation/virtual/kvm/amd-memory-encryption.txt
diff --git a/Documentation/virtual/kvm/amd-memory-encryption.txt b/Documentation/virtual/kvm/amd-memory-encryption.txt
new file mode 100644
index 0000000..cffed2d
--- /dev/null
+++ b/Documentation/virtual/kvm/amd-memory-encryption.txt
@@ -0,0 +1,328 @@
+Secure Encrypted Virtualization (SEV) is a feature found on AMD processors.
+
+SEV is an extension to the AMD-V architecture which supports running virtual
+machine (VMs) under the control of a hypervisor. When enabled, the memory
+contents of VM will be transparently encrypted with a key unique to the VM.
+
+Hypervisor can determine the SEV support through the CPUID instruction. The CPUID
+function 0x8000001f reports information related to SEV:
+
+ 0x8000001f[eax]:
+ Bit[1] indicates support for SEV
+
+ 0x8000001f[ecx]:
+ Bits[31:0] Number of encrypted guest supported simultaneously
+
+If support for SEV is present, MSR 0xc00100010 (MSR_K8_SYSCFG) and MSR
+0xc0000015 (MSR_K7_HWCR_SMMLOCK) can be used to determine if it can be enabled:
+
+ 0xc00100010:
+ Bit[23] 0 = memory encryption can be enabled
+ 0 = memory encryption can not be enabled
+
+ 0xc00010015:
+ Bit[0] 0 = memory encryption can not be enabled
+ 1 = memory encryption can be enabled
+
+When SEV support is available, it can be enabled on specific VM during the VMRUN
+instruction by setting SEV bit in VMCB offset 090h:
+
+ VMCB offset 090h:
+ Bit[1] 1 = Enable SEV
+
+SEV hardware uses ASIDs to associate memory encryption key with the guest VMs.
+Hence the ASID for the SEV-enabled guests must be from 1 to a maximum value
+defined through the CPUID function 0x8000001f[ECX].
+
+
+SEV Key Management
+------------------
+
+The Key management for the SEV guest is handled by a seperate processor known as
+the AMD Secure Processor (AMD-SP). Firmware running inside the AMD-SP provides a
+secure key management interface to perform common hypervisor activities such as
+encrypting bootstrap code, snapshotting, migrating and debugging the guest. For
+more informaiton, see SEV Key Management spec:
+
+http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
+
+1. KVM_SEV_LAUNCH_START
+
+Parameters: struct kvm_sev_launch_start (in/out)
+Returns: 0 on success, -negative on error
+
+LAUNCH_START command is used to bootstrap a guest by encrypting its memory with
+a new VM Encryption Key (VEK). In order to create guest context, hypervisor should
+provide guest policy, owners public diffie-hellman (PDH) key and session parameters.
+
+The guest policy constrains the use and features activated for the lifetime of the
+launched guest, such as disallowing debugging, enabling key sharing, or turning on
+other SEV related features.
+
+The guest owners PDH allows the firmware to establish a cryptographic session with
+the guest owner to negotiate keys used for attestation.
+
+The session parameters contains informations such as guest policy MAC, transport
+integrity key (TIK), transport encryption key (TEK) etc.
+
+struct kvm_sev_launch_start {
+
+ /* Guest Hanldle, if zero then FW creates a new handle */
+ __u32 handle;
+
+ /* Guest policy */
+ __u32 policy;
+
+ /* Address which contains guest owner's PDH certificate blob */
+ __u64 dh_cert_address;
+ __u32 dh_cert_length;
+
+ /* Address which contains guest session information blob */
+ __u64 session_address;
+ __u32 session_length;
+};
+
+On success, the 'handle' field contain a new handle.
+
+2. KVM_SEV_LAUNCH_UPDATE_DATA
+
+Parameters (in): struct kvm_sev_launch_update
+Returns: 0 on success, -negative on error
+
+LAUNCH_UPDATE_DATA encrypts the memory region using the VEK created during
+LAUNCH_START. It also calculates a measurement of the memory region. This
+measurement can be used as a signature of the memory contents.
+
+struct kvm_sev_launch_update {
+ /* address of the data to be encrypted (must be 16-byte aligned) */
+ __u64 address;
+
+ /* length of the data to be encrypted (must be 16-byte aligned) */
+ __u32 length;
+};
+
+3. KVM_SEV_LAUNCH_MEASURE
+
+Parameters (in): struct kvm_sev_launch_measure
+Returns: 0 on success, -negative on error
+
+LAUNCH_MEASURE returns the measurement of the memory region encrypted with
+LAUNCH_UPDATE_DATA. The measurement is keyed with the TIK so that the guest
+owner can use the measurement to verify the guest was properly launched without
+tempering.
+
+struct kvm_sev_launch_measure {
+ /* where to copy the measurement blob */
+ __u64 address;
+
+ /* length of memory region containing measurement */
+ __u32 length;
+};
+
+If measurement length is too small, the required length is returned in the
+length field.
+
+On success, the measurement is copied to the address.
+
+4. KVM_SEV_LAUNCH_FINISH
+
+Returns: 0 on success, -negative on error
+
+LAUNCH_FINISH command finalize the SEV guest launch process.
+
+5. KVM_SEV_GUEST_STATUS
+
+Parameters (out): struct kvm_sev_guest_status
+Returns: 0 on success, -negative on error
+
+GUEST_STATUS returns the current SEV state the guest is in.
+
+struct kvm_sev_guest_status {
+
+ /* guest hanldle */
+ __u32 handle;
+
+ /* guest policy */
+ __u32 policy;
+
+ /* guest state (see below) */
+ __u8 state;
+};
+
+SEV guest state:
+
+enum {
+ /* guest state is not known */
+ SEV_STATE_INVALID = 0;
+ /* guest is currently being launched */
+ SEV_STATE_LAUNCHING.
+ /* guest is being launched and ready to accept the ciphertext data */
+ SEV_STATE_SECRET,
+ /* guest is fully launched and running */
+ SEV_STATE_RUNNING,
+ /* guest is being migrated in from another SEV machine */
+ SEV_STATE_RECEIVING,
+ /* guest is getting migrated out another SEV machine */
+ SEV_STATE_SENDING
+};
+
+6. KVM_SEV_DBG_DECRYPT
+
+DEBUG_DECRYPT command can be used for decrypting a region of guest memory for
+the SEV guest debug purposes. Note that since decrypting protected memory allows
+the hypervisor to gain access to guest memory, the guest policy must explicitly
+allow debugging for this command to work.
+
+Parameters (in): struct kvm_sev_dbg
+Returns: 0 on success, -negative on error
+
+struct kvm_sev_dbg {
+ __u64 src_address;
+ __u64 dst_address;
+
+ /* length of memory region to decrypt */
+ __u32 length;
+};
+
+7. KVM_SEV_DBG_ENCRYPT
+
+DEBUG_ENCRYPT command can be used for injecting the data into guest for the SEV
+guest debug purposes. Note that since injecting the data into protected memory
+allows the hypervisor to modify the guest memory, the guest policy must explicitly
+allow debugging for this command to work.
+
+Parameters (in): struct kvm_sev_dbg
+Returns: 0 on success, -negative on error
+
+struct kvm_sev_dbg {
+ __u64 src_address;
+ __u64 dst_address;
+
+ /* length of memory region to encrypt */
+ __u32 length;
+};
+
+8. KVM_SEV_SEND_START
+
+Parameters (in): struct kvm_sev_send_start
+Returns: 0 on success, -negative on error
+
+SEND_START command is used to export a SEV guest from one platform to another.
+It can be used for saving a guest to disk to be resumed later, or it can be
+used to migrate a guest across the network to a receiving platform.
+
+struct kvm_sev_send_start {
+ /* guest policy */
+ __u32 policy;
+
+ /* address which contains receivers PDH key blob */
+ __u64 pdh_cert_address;
+ __u32 pdh_cert_length;
+
+ /* address which contains platform certificate blob */
+ __u64 plat_cert_address;
+ __u32 plat_cert_length;
+
+ /* address which contains AMD certificate chain */
+ __u64 amd_cert_address;
+ __u32 amd_cert_length;
+
+ /* where to copy the current session information */
+ __u64 session_address;
+ __u32 session_length;
+};
+
+The command uses PDH key to establish a new cryptographic context with the
+remote platform - the new cryptographic context will be used for re-encrypting
+the guest memory before sending it to remote platform.
+
+If length of the certificate blobs are too small, the required length is
+returned in the length field and an error is returned.
+
+9. KVM_SEV_SEND_UPDATE_DATA
+
+Parameters (in): struct kvm_sev_send_update_data
+Returns: 0 on success, -negative on error
+
+SEND_UPDATE_DATA command is used to re-encrypt the guest memory using the
+crytographic context established during SEND_START. A fresh IV is generated
+and written to the packet header field.
+
+struct kvm_sev_send_update_data {
+ /* address which will contain packet header (IV, MAC etc)*/
+ __u64 hdr_data;
+ __u32 hdr_length;
+
+ /* address of guest memory region containg encrypted data */
+ __u64 guest_address;
+ __u32 guest_length;
+
+ /* address of transport buffer */
+ __u64 host_address;
+ __u32 host_length;
+};
+
+If the hdr_length is too small, the required length is returned in the length
+field and an error is returned.
+
+10. KVM_SEV_SEND_FINISH
+
+Returns: 0 on success, -negative on error
+
+SEND_FINISH command finalize the SEV guest sending process.
+
+11. KVM_SEV_RECEIVE_START
+
+Parameters (in): struct kvm_sev_receive_start
+Returns: 0 on success, -negative on error
+
+RECEIVE_START command is used to import a guest from one platform to another.
+It can be used for restoring a guest from disk, or it can be used to migrate
+a guest across the network from a sending platform.
+
+struct kvm_sev_receive_start {
+ /* guest handle (if zero then new handle will be created) */
+ __u32 handle;
+
+ /* guest policy */
+ __u32 policy;
+
+ /* Address containing senders PDH certificate blob */
+ __u64 pdh_cert_address;
+ __u32 pdh_cert_length;
+
+ /* Address containing sender's session information blob */
+ __u64 session_address;
+ __u32 session_length;
+};
+
+The RECEIVE_START command creates a new cryptographic context necessary to
+re-enrypt the guest memory receieved through the RECEIVE_UPDATE command.
+
+12. KVM_SEV_RECEIVE_UPDATE_DATA
+
+Parameters (in): struct kvm_sev_receive_update_data
+Returns: 0 on success, -negative on error
+
+RECEIVE_UPDATE_DATA command is used to re-encrypt the guest memory using the
+crytographic context established during RECEIVE_START.
+
+struct kvm_sev_receive_update_data {
+ /* packet header receieved from the SEND_UPDATE_DATA command */
+ __u64 hdr_data;
+ __u32 hdr_length;
+
+ /* address of guest memory region */
+ __u64 guest_address;
+ __u32 guest_length;
+
+ /* address of transport buffer */
+ __u64 host_address;
+ __u32 host_length;
+};
+
+13. KVM_SEV_RECEIVE_FINISH
+
+Returns: 0 on success, -negative on error
+
+RECEIVE_FINISH command finalize the SEV guest receiving process.
--
2.9.4
Platform Security Processor (PSP) is part of AMD Secure Processor (AMD-SP),
PSP is a dedicated processor that provides the support for key management
commands in a Secure Encrypted Virtualiztion (SEV) mode, along with
software-based Tursted Executation Environment (TEE) to enable the
third-party tursted applications.
Cc: Herbert Xu <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: Gary Hook <[email protected]>
Cc: [email protected]
Signed-off-by: Brijesh Singh <[email protected]>
---
drivers/crypto/ccp/Kconfig | 9 ++
drivers/crypto/ccp/Makefile | 1 +
drivers/crypto/ccp/psp-dev.c | 226 +++++++++++++++++++++++++++++++++++++++++++
drivers/crypto/ccp/psp-dev.h | 82 ++++++++++++++++
drivers/crypto/ccp/sp-dev.c | 43 ++++++++
drivers/crypto/ccp/sp-dev.h | 41 +++++++-
drivers/crypto/ccp/sp-pci.c | 46 +++++++++
7 files changed, 447 insertions(+), 1 deletion(-)
create mode 100644 drivers/crypto/ccp/psp-dev.c
create mode 100644 drivers/crypto/ccp/psp-dev.h
diff --git a/drivers/crypto/ccp/Kconfig b/drivers/crypto/ccp/Kconfig
index 15b63fd..41c0ff5 100644
--- a/drivers/crypto/ccp/Kconfig
+++ b/drivers/crypto/ccp/Kconfig
@@ -31,3 +31,12 @@ 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 device"
+ default y
+ depends on CRYPTO_DEV_CCP_DD
+ help
+ Provide the support for AMD Platform Security Processor (PSP) device
+ which can be used for communicating with Secure Encryption Virtualization
+ (SEV) firmware.
diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile
index 5f2adc5..8aae4ff 100644
--- a/drivers/crypto/ccp/Makefile
+++ b/drivers/crypto/ccp/Makefile
@@ -7,6 +7,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 \
diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c
new file mode 100644
index 0000000..bb0ea9a
--- /dev/null
+++ b/drivers/crypto/ccp/psp-dev.c
@@ -0,0 +1,226 @@
+/*
+ * AMD Platform Security Processor (PSP) interface
+ *
+ * Copyright (C) 2016 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <[email protected]>
+ *
+ * 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"
+
+static LIST_HEAD(psp_devs);
+static DEFINE_SPINLOCK(psp_devs_lock);
+
+const struct psp_vdata psp_entry = {
+ .offset = 0x10500,
+};
+
+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);
+
+ spin_unlock_irqrestore(&psp_devs_lock, flags);
+}
+
+void psp_del_device(struct psp_device *psp)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&psp_devs_lock, flags);
+
+ list_del(&psp->entry);
+ spin_unlock_irqrestore(&psp_devs_lock, flags);
+}
+
+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;
+}
+
+irqreturn_t psp_irq_handler(int irq, void *data)
+{
+ unsigned int status;
+ irqreturn_t ret = IRQ_HANDLED;
+ struct psp_device *psp = data;
+
+ /* read the interrupt status */
+ status = ioread32(psp->io_regs + PSP_P2CMSG_INTSTS);
+
+ /* invoke subdevice interrupt handlers */
+ if (status) {
+ if (psp->sev_irq_handler)
+ ret = psp->sev_irq_handler(irq, psp->sev_irq_data);
+ if (psp->tee_irq_handler)
+ ret = psp->tee_irq_handler(irq, psp->tee_irq_data);
+ }
+
+ /* clear the interrupt status */
+ iowrite32(status, psp->io_regs + PSP_P2CMSG_INTSTS);
+
+ return ret;
+}
+
+static int psp_init(struct psp_device *psp)
+{
+ psp_add_device(psp);
+
+ return 0;
+}
+
+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(0xffffffff, psp->io_regs + PSP_P2CMSG_INTSTS);
+
+ dev_dbg(dev, "requesting an IRQ ...\n");
+ /* 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;
+ }
+
+ sp_set_psp_master(sp);
+
+ dev_dbg(dev, "initializing psp\n");
+ ret = psp_init(psp);
+ if (ret) {
+ dev_err(dev, "failed to init psp\n");
+ goto e_irq;
+ }
+
+ /* Enable interrupt */
+ dev_dbg(dev, "Enabling interrupts ...\n");
+ iowrite32(7, psp->io_regs + PSP_P2CMSG_INTEN);
+
+ dev_notice(dev, "psp enabled\n");
+
+ 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;
+
+ sp_free_psp_irq(sp, psp);
+
+ psp_del_device(psp);
+}
+
+int psp_dev_resume(struct sp_device *sp)
+{
+ return 0;
+}
+
+int psp_dev_suspend(struct sp_device *sp, pm_message_t state)
+{
+ return 0;
+}
+
+int psp_request_tee_irq(struct psp_device *psp, irq_handler_t handler,
+ void *data)
+{
+ psp->tee_irq_data = data;
+ psp->tee_irq_handler = handler;
+
+ return 0;
+}
+
+int psp_free_tee_irq(struct psp_device *psp, void *data)
+{
+ if (psp->tee_irq_handler) {
+ psp->tee_irq_data = NULL;
+ psp->tee_irq_handler = NULL;
+ }
+
+ return 0;
+}
+
+int psp_request_sev_irq(struct psp_device *psp, irq_handler_t handler,
+ void *data)
+{
+ psp->sev_irq_data = data;
+ psp->sev_irq_handler = handler;
+
+ return 0;
+}
+
+int psp_free_sev_irq(struct psp_device *psp, void *data)
+{
+ if (psp->sev_irq_handler) {
+ psp->sev_irq_data = NULL;
+ psp->sev_irq_handler = NULL;
+ }
+
+ return 0;
+}
+
+struct psp_device *psp_get_master_device(void)
+{
+ struct sp_device *sp = sp_get_psp_master_device();
+
+ return sp ? sp->psp_data : NULL;
+}
diff --git a/drivers/crypto/ccp/psp-dev.h b/drivers/crypto/ccp/psp-dev.h
new file mode 100644
index 0000000..6e167b8
--- /dev/null
+++ b/drivers/crypto/ccp/psp-dev.h
@@ -0,0 +1,82 @@
+/*
+ * AMD Platform Security Processor (PSP) interface driver
+ *
+ * Copyright (C) 2017 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <[email protected]>
+ *
+ * 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 "sp-dev.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_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 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;
+
+ irq_handler_t sev_irq_handler;
+ void *sev_irq_data;
+ irq_handler_t tee_irq_handler;
+ void *tee_irq_data;
+
+ void *sev_data;
+ void *tee_data;
+};
+
+void psp_add_device(struct psp_device *psp);
+void psp_del_device(struct psp_device *psp);
+
+int psp_request_sev_irq(struct psp_device *psp, irq_handler_t handler,
+ void *data);
+int psp_free_sev_irq(struct psp_device *psp, void *data);
+
+int psp_request_tee_irq(struct psp_device *psp, irq_handler_t handler,
+ void *data);
+int psp_free_tee_irq(struct psp_device *psp, void *data);
+
+struct psp_device *psp_get_master_device(void);
+
+extern const struct psp_vdata psp_entry;
+
+#endif /* __PSP_DEV_H */
diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c
index a017233..d263ba4 100644
--- a/drivers/crypto/ccp/sp-dev.c
+++ b/drivers/crypto/ccp/sp-dev.c
@@ -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);
}
@@ -219,6 +224,12 @@ int sp_suspend(struct sp_device *sp, pm_message_t state)
return ret;
}
+ if (sp->dev_vdata->psp_vdata) {
+ ret = psp_dev_suspend(sp, state);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -232,9 +243,41 @@ int sp_resume(struct sp_device *sp)
return ret;
}
+ if (sp->dev_vdata->psp_vdata) {
+ ret = psp_dev_resume(sp);
+ if (ret)
+ return ret;
+ }
return 0;
}
+struct sp_device *sp_get_psp_master_device(void)
+{
+ unsigned long flags;
+ struct sp_device *i, *ret = NULL;
+
+ 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;
+}
+
+void sp_set_psp_master(struct sp_device *sp)
+{
+ if (sp->set_psp_master_device)
+ sp->set_psp_master_device(sp);
+}
+
static int __init sp_mod_init(void)
{
#ifdef CONFIG_X86
diff --git a/drivers/crypto/ccp/sp-dev.h b/drivers/crypto/ccp/sp-dev.h
index 3520da4..f98a3f9 100644
--- a/drivers/crypto/ccp/sp-dev.h
+++ b/drivers/crypto/ccp/sp-dev.h
@@ -41,12 +41,19 @@ struct ccp_vdata {
const struct ccp_actions *perform;
const unsigned int offset;
};
+
+struct psp_vdata {
+ const unsigned int version;
+ const struct psp_actions *perform;
+ 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 {
@@ -67,6 +74,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;
@@ -102,6 +113,8 @@ 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);
+void sp_set_psp_master(struct sp_device *sp);
+struct sp_device *sp_get_psp_master_device(void);
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
@@ -129,4 +142,30 @@ 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_dev_destroy(struct sp_device *sp);
+
+int psp_dev_suspend(struct sp_device *sp, pm_message_t state);
+int psp_dev_resume(struct sp_device *sp);
+#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
+
+static inline int psp_dev_init(struct sp_device *sp)
+{
+ return 0;
+}
+static inline void psp_dev_destroy(struct sp_device *sp) { }
+
+static inline int psp_dev_suspend(struct sp_device *sp, pm_message_t state)
+{
+ return 0;
+}
+static inline int psp_dev_resume(struct sp_device *sp)
+{
+ return 0;
+}
+
+#endif /* CONFIG_CRYPTO_DEV_SP_PSP */
+
#endif
diff --git a/drivers/crypto/ccp/sp-pci.c b/drivers/crypto/ccp/sp-pci.c
index 9859aa6..e58b3ad 100644
--- a/drivers/crypto/ccp/sp-pci.c
+++ b/drivers/crypto/ccp/sp-pci.c
@@ -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) {
@@ -237,6 +280,9 @@ static const struct sp_dev_vdata dev_vdata[] = {
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
.ccp_vdata = &ccpv5a,
#endif
+#ifdef CONFIG_CRYPTO_DEV_PSP
+ .psp_vdata = &psp_entry
+#endif
},
{
.bar = 2,
--
2.9.4
SEV-enabled guest must use ASIDs from the defined subset, while non-SEV
guests can use the remaining ASID range. The range of ASID allowed for
SEV-enabled guest is from 1 to a maximum value defined via CPUID
Fn8000_001f[ECX].
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 46f41bb..06bd902 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -319,6 +319,9 @@ enum {
#define VMCB_AVIC_APIC_BAR_MASK 0xFFFFFFFFFF000ULL
+/* Secure Encrypted Virtualization */
+static unsigned int max_sev_asid;
+
static inline void mark_all_dirty(struct vmcb *vmcb)
{
vmcb->control.clean = 0;
@@ -769,7 +772,7 @@ static int svm_hardware_enable(void)
sd->asid_generation = 1;
sd->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1;
sd->next_asid = sd->max_asid + 1;
- sd->min_asid = 1;
+ sd->min_asid = max_sev_asid + 1;
gdt = get_current_gdt_rw();
sd->tss_desc = (struct kvm_ldttss_desc *)(gdt + GDT_ENTRY_TSS);
@@ -1033,6 +1036,21 @@ static int avic_ga_log_notifier(u32 ga_tag)
return 0;
}
+static __init void sev_hardware_setup(void)
+{
+ int nguests;
+
+ /*
+ * Get maximum number of encrypted guest supported: Fn8001_001F[ECX]
+ * Bit 31:0: Number of supported guest
+ */
+ nguests = cpuid_ecx(0x8000001F);
+ if (!nguests)
+ return;
+
+ max_sev_asid = nguests;
+}
+
static __init int svm_hardware_setup(void)
{
int cpu;
@@ -1063,6 +1081,9 @@ static __init int svm_hardware_setup(void)
kvm_tsc_scaling_ratio_frac_bits = 32;
}
+ if (boot_cpu_has(X86_FEATURE_SEV))
+ sev_hardware_setup();
+
if (nested) {
printk(KERN_INFO "kvm: Nested Virtualization enabled\n");
kvm_enable_efer_bits(EFER_SVME | EFER_LMSLE);
--
2.9.4
AMDs new Secure Encrypted Virtualization (SEV) feature allows the memory
contents of a virtual machine to be transparently encrypted with a key
unique to the guest VM. The programming and management of the encryption
keys are handled by the AMD Secure Processor (AMD-SP), which exposes the
commands for these tasks. The complete spec for various commands are
available at:
http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
This patch extends AMD-SP driver to provide:
- a in-kernel APIs to communicate with SEV device. The APIs can be used
by the hypervisor to create encryption context for the SEV guests.
- a userspace IOCTL to manage the platform certificates etc
Cc: Herbert Xu <[email protected]>
Cc: David S. Miller <[email protected]>
Cc: Gary Hook <[email protected]>
Cc: [email protected]
Signed-off-by: Brijesh Singh <[email protected]>
---
drivers/crypto/ccp/Kconfig | 10 +
drivers/crypto/ccp/Makefile | 1 +
drivers/crypto/ccp/psp-dev.c | 4 +
drivers/crypto/ccp/psp-dev.h | 27 ++
drivers/crypto/ccp/sev-dev.c | 416 ++++++++++++++++++++++++++
drivers/crypto/ccp/sev-dev.h | 67 +++++
drivers/crypto/ccp/sev-ops.c | 457 +++++++++++++++++++++++++++++
drivers/crypto/ccp/sp-pci.c | 2 +-
include/linux/psp-sev.h | 683 +++++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/psp-sev.h | 110 +++++++
10 files changed, 1776 insertions(+), 1 deletion(-)
create mode 100644 drivers/crypto/ccp/sev-dev.c
create mode 100644 drivers/crypto/ccp/sev-dev.h
create mode 100644 drivers/crypto/ccp/sev-ops.c
create mode 100644 include/linux/psp-sev.h
create mode 100644 include/uapi/linux/psp-sev.h
diff --git a/drivers/crypto/ccp/Kconfig b/drivers/crypto/ccp/Kconfig
index 41c0ff5..ae0ff1c 100644
--- a/drivers/crypto/ccp/Kconfig
+++ b/drivers/crypto/ccp/Kconfig
@@ -40,3 +40,13 @@ config CRYPTO_DEV_SP_PSP
Provide the support for AMD Platform Security Processor (PSP) device
which can be used for communicating with Secure Encryption Virtualization
(SEV) firmware.
+
+config CRYPTO_DEV_PSP_SEV
+ bool "Secure Encrypted Virtualization (SEV) interface"
+ default y
+ depends on CRYPTO_DEV_CCP_DD
+ depends on CRYPTO_DEV_SP_PSP
+ help
+ Provide the kernel and userspace (/dev/sev) interface to communicate with
+ Secure Encrypted Virtualization (SEV) firmware running inside AMD Platform
+ Security Processor (PSP)
diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile
index 8aae4ff..94ca748 100644
--- a/drivers/crypto/ccp/Makefile
+++ b/drivers/crypto/ccp/Makefile
@@ -8,6 +8,7 @@ ccp-$(CONFIG_CRYPTO_DEV_SP_CCP) += ccp-dev.o \
ccp-debugfs.o
ccp-$(CONFIG_PCI) += sp-pci.o
ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += psp-dev.o
+ccp-$(CONFIG_CRYPTO_DEV_PSP_SEV) += sev-dev.o sev-ops.o
obj-$(CONFIG_CRYPTO_DEV_CCP_CRYPTO) += ccp-crypto.o
ccp-crypto-objs := ccp-crypto-main.o \
diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c
index bb0ea9a..0c9d25c 100644
--- a/drivers/crypto/ccp/psp-dev.c
+++ b/drivers/crypto/ccp/psp-dev.c
@@ -97,6 +97,7 @@ irqreturn_t psp_irq_handler(int irq, void *data)
static int psp_init(struct psp_device *psp)
{
psp_add_device(psp);
+ sev_dev_init(psp);
return 0;
}
@@ -166,17 +167,20 @@ void psp_dev_destroy(struct sp_device *sp)
struct psp_device *psp = sp->psp_data;
sp_free_psp_irq(sp, psp);
+ sev_dev_destroy(psp);
psp_del_device(psp);
}
int psp_dev_resume(struct sp_device *sp)
{
+ sev_dev_resume(sp->psp_data);
return 0;
}
int psp_dev_suspend(struct sp_device *sp, pm_message_t state)
{
+ sev_dev_suspend(sp->psp_data, state);
return 0;
}
diff --git a/drivers/crypto/ccp/psp-dev.h b/drivers/crypto/ccp/psp-dev.h
index 6e167b8..9334d87 100644
--- a/drivers/crypto/ccp/psp-dev.h
+++ b/drivers/crypto/ccp/psp-dev.h
@@ -78,5 +78,32 @@ int psp_free_tee_irq(struct psp_device *psp, void *data);
struct psp_device *psp_get_master_device(void);
extern const struct psp_vdata psp_entry;
+#ifdef CONFIG_CRYPTO_DEV_PSP_SEV
+
+int sev_dev_init(struct psp_device *psp);
+void sev_dev_destroy(struct psp_device *psp);
+int sev_dev_resume(struct psp_device *psp);
+int sev_dev_suspend(struct psp_device *psp, pm_message_t state);
+
+#else /* !CONFIG_CRYPTO_DEV_PSP_SEV */
+
+static inline int sev_dev_init(struct psp_device *psp)
+{
+ return -ENODEV;
+}
+
+static inline void sev_dev_destroy(struct psp_device *psp) { }
+
+static inline int sev_dev_resume(struct psp_device *psp)
+{
+ return -ENODEV;
+}
+
+static inline int sev_dev_suspend(struct psp_device *psp, pm_message_t state)
+{
+ return -ENODEV;
+}
+
+#endif /* CONFIG_CRYPTO_DEV_PSP_SEV */
#endif /* __PSP_DEV_H */
diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
new file mode 100644
index 0000000..a2b41dd
--- /dev/null
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -0,0 +1,416 @@
+/*
+ * AMD Secure Encrypted Virtualization (SEV) interface
+ *
+ * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <[email protected]>
+ *
+ * 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/wait.h>
+#include <linux/jiffies.h>
+
+#include "psp-dev.h"
+#include "sev-dev.h"
+
+extern const struct file_operations sev_fops;
+
+static LIST_HEAD(sev_devs);
+static DEFINE_SPINLOCK(sev_devs_lock);
+static atomic_t sev_id;
+
+static unsigned int sev_poll;
+module_param(sev_poll, uint, 0444);
+MODULE_PARM_DESC(sev_poll, "Poll for sev command completion - any non-zero value");
+
+DEFINE_MUTEX(sev_cmd_mutex);
+
+void sev_add_device(struct sev_device *sev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sev_devs_lock, flags);
+
+ list_add_tail(&sev->entry, &sev_devs);
+
+ spin_unlock_irqrestore(&sev_devs_lock, flags);
+}
+
+void sev_del_device(struct sev_device *sev)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&sev_devs_lock, flags);
+
+ list_del(&sev->entry);
+ spin_unlock_irqrestore(&sev_devs_lock, flags);
+}
+
+static struct sev_device *get_sev_master_device(void)
+{
+ struct psp_device *psp = psp_get_master_device();
+
+ return psp ? psp->sev_data : NULL;
+}
+
+static int sev_wait_cmd_poll(struct sev_device *sev, unsigned int timeout,
+ unsigned int *reg)
+{
+ int wait = timeout * 10; /* 100ms sleep => timeout * 10 */
+
+ while (--wait) {
+ msleep(100);
+
+ *reg = ioread32(sev->io_regs + PSP_CMDRESP);
+ if (*reg & PSP_CMDRESP_RESP)
+ break;
+ }
+
+ if (!wait) {
+ dev_err(sev->dev, "sev command timed out\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int sev_wait_cmd_ioc(struct sev_device *sev, unsigned int *reg)
+{
+ sev->int_rcvd = 0;
+
+ wait_event(sev->int_queue, sev->int_rcvd);
+ *reg = ioread32(sev->io_regs + PSP_CMDRESP);
+
+ return 0;
+}
+
+static int sev_wait_cmd(struct sev_device *sev, unsigned int *reg)
+{
+ return (*reg & PSP_CMDRESP_IOC) ? sev_wait_cmd_ioc(sev, reg)
+ : sev_wait_cmd_poll(sev, 10, reg);
+}
+
+static struct sev_device *sev_alloc_struct(struct psp_device *psp)
+{
+ struct device *dev = psp->dev;
+ struct sev_device *sev;
+
+ sev = devm_kzalloc(dev, sizeof(*sev), GFP_KERNEL);
+ if (!sev)
+ return NULL;
+
+ sev->dev = dev;
+ sev->psp = psp;
+ sev->id = atomic_inc_return(&sev_id);
+
+ snprintf(sev->name, sizeof(sev->name), "sev%u", sev->id);
+ init_waitqueue_head(&sev->int_queue);
+
+ return sev;
+}
+
+irqreturn_t sev_irq_handler(int irq, void *data)
+{
+ struct sev_device *sev = data;
+ unsigned int status;
+
+ status = ioread32(sev->io_regs + PSP_P2CMSG_INTSTS);
+ if (status & (1 << PSP_CMD_COMPLETE_REG)) {
+ int reg;
+
+ reg = ioread32(sev->io_regs + PSP_CMDRESP);
+ if (reg & PSP_CMDRESP_RESP) {
+ sev->int_rcvd = 1;
+ wake_up(&sev->int_queue);
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+static bool check_sev_support(struct sev_device *sev)
+{
+ /* Bit 0 in PSP_FEATURE_REG is set then SEV is support in PSP */
+ if (ioread32(sev->io_regs + PSP_FEATURE_REG) & 1)
+ return true;
+
+ return false;
+}
+
+int sev_dev_init(struct psp_device *psp)
+{
+ struct device *dev = psp->dev;
+ struct sev_device *sev;
+ int ret;
+
+ ret = -ENOMEM;
+ sev = sev_alloc_struct(psp);
+ if (!sev)
+ goto e_err;
+ psp->sev_data = sev;
+
+ sev->io_regs = psp->io_regs;
+
+ dev_dbg(dev, "checking SEV support ...\n");
+ /* check SEV support */
+ if (!check_sev_support(sev)) {
+ dev_dbg(dev, "device does not support SEV\n");
+ goto e_err;
+ }
+
+ dev_dbg(dev, "requesting an IRQ ...\n");
+ /* Request an irq */
+ ret = psp_request_sev_irq(sev->psp, sev_irq_handler, sev);
+ if (ret) {
+ dev_err(dev, "unable to allocate an IRQ\n");
+ goto e_err;
+ }
+
+ /* initialize SEV ops */
+ dev_dbg(dev, "init sev ops\n");
+ ret = sev_ops_init(sev);
+ if (ret) {
+ dev_err(dev, "failed to init sev ops\n");
+ goto e_irq;
+ }
+
+ sev_add_device(sev);
+
+ dev_notice(dev, "sev enabled\n");
+
+ return 0;
+
+e_irq:
+ psp_free_sev_irq(psp, sev);
+e_err:
+ psp->sev_data = NULL;
+
+ dev_notice(dev, "sev initialization failed\n");
+
+ return ret;
+}
+
+void sev_dev_destroy(struct psp_device *psp)
+{
+ struct sev_device *sev = psp->sev_data;
+
+ psp_free_sev_irq(psp, sev);
+
+ sev_ops_destroy(sev);
+
+ sev_del_device(sev);
+}
+
+int sev_dev_resume(struct psp_device *psp)
+{
+ return 0;
+}
+
+int sev_dev_suspend(struct psp_device *psp, pm_message_t state)
+{
+ return 0;
+}
+
+static int sev_cmd_buffer_len(int cmd)
+{
+ int size;
+
+ switch (cmd) {
+ case SEV_CMD_INIT:
+ size = sizeof(struct sev_data_init);
+ break;
+ case SEV_CMD_PLATFORM_STATUS:
+ size = sizeof(struct sev_data_status);
+ break;
+ case SEV_CMD_PEK_CSR:
+ size = sizeof(struct sev_data_pek_csr);
+ break;
+ case SEV_CMD_PEK_CERT_IMPORT:
+ size = sizeof(struct sev_data_pek_cert_import);
+ break;
+ case SEV_CMD_PDH_CERT_EXPORT:
+ size = sizeof(struct sev_data_pdh_cert_export);
+ break;
+ case SEV_CMD_LAUNCH_START:
+ size = sizeof(struct sev_data_launch_start);
+ break;
+ case SEV_CMD_LAUNCH_UPDATE_DATA:
+ size = sizeof(struct sev_data_launch_update_data);
+ break;
+ case SEV_CMD_LAUNCH_UPDATE_VMSA:
+ size = sizeof(struct sev_data_launch_update_vmsa);
+ break;
+ case SEV_CMD_LAUNCH_FINISH:
+ size = sizeof(struct sev_data_launch_finish);
+ break;
+ case SEV_CMD_LAUNCH_UPDATE_SECRET:
+ size = sizeof(struct sev_data_launch_secret);
+ break;
+ case SEV_CMD_LAUNCH_MEASURE:
+ size = sizeof(struct sev_data_launch_measure);
+ break;
+ case SEV_CMD_ACTIVATE:
+ size = sizeof(struct sev_data_activate);
+ break;
+ case SEV_CMD_DEACTIVATE:
+ size = sizeof(struct sev_data_deactivate);
+ break;
+ case SEV_CMD_DECOMMISSION:
+ size = sizeof(struct sev_data_decommission);
+ break;
+ case SEV_CMD_GUEST_STATUS:
+ size = sizeof(struct sev_data_guest_status);
+ break;
+ case SEV_CMD_DBG_DECRYPT:
+ case SEV_CMD_DBG_ENCRYPT:
+ size = sizeof(struct sev_data_dbg);
+ break;
+ case SEV_CMD_SEND_START:
+ size = sizeof(struct sev_data_send_start);
+ break;
+ case SEV_CMD_SEND_UPDATE_DATA:
+ size = sizeof(struct sev_data_send_update_data);
+ break;
+ case SEV_CMD_SEND_UPDATE_VMSA:
+ size = sizeof(struct sev_data_send_update_vmsa);
+ break;
+ case SEV_CMD_SEND_FINISH:
+ size = sizeof(struct sev_data_send_finish);
+ break;
+ case SEV_CMD_RECEIVE_START:
+ size = sizeof(struct sev_data_receive_start);
+ break;
+ case SEV_CMD_RECEIVE_UPDATE_DATA:
+ size = sizeof(struct sev_data_receive_update_data);
+ break;
+ case SEV_CMD_RECEIVE_UPDATE_VMSA:
+ size = sizeof(struct sev_data_receive_update_vmsa);
+ break;
+ case SEV_CMD_RECEIVE_FINISH:
+ size = sizeof(struct sev_data_receive_finish);
+ break;
+ default:
+ size = 0;
+ break;
+ }
+
+ return size;
+}
+
+int sev_issue_cmd(int cmd, void *data, int *psp_ret)
+{
+ struct sev_device *sev = get_sev_master_device();
+ unsigned int phys_lsb, phys_msb;
+ unsigned int reg, ret;
+
+ if (!sev)
+ return -ENODEV;
+
+ if (psp_ret)
+ *psp_ret = 0;
+
+ /* 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;
+
+ dev_dbg(sev->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);
+
+ /* Only one command at a time... */
+ mutex_lock(&sev_cmd_mutex);
+
+ iowrite32(phys_lsb, sev->io_regs + PSP_CMDBUFF_ADDR_LO);
+ iowrite32(phys_msb, sev->io_regs + PSP_CMDBUFF_ADDR_HI);
+ wmb();
+
+ reg = cmd;
+ reg <<= PSP_CMDRESP_CMD_SHIFT;
+ reg |= sev_poll ? 0 : PSP_CMDRESP_IOC;
+ iowrite32(reg, sev->io_regs + PSP_CMDRESP);
+
+ ret = sev_wait_cmd(sev, ®);
+ if (ret)
+ goto unlock;
+
+ if (psp_ret)
+ *psp_ret = reg & PSP_CMDRESP_ERR_MASK;
+
+ if (reg & PSP_CMDRESP_ERR_MASK) {
+ dev_dbg(sev->dev, "sev command %u failed (%#010x)\n",
+ cmd, reg & PSP_CMDRESP_ERR_MASK);
+ ret = -EIO;
+ }
+
+unlock:
+ mutex_unlock(&sev_cmd_mutex);
+ print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data,
+ sev_cmd_buffer_len(cmd), false);
+ return ret;
+}
+
+int sev_platform_init(struct sev_data_init *data, int *error)
+{
+ return sev_issue_cmd(SEV_CMD_INIT, data, error);
+}
+EXPORT_SYMBOL_GPL(sev_platform_init);
+
+int sev_platform_shutdown(int *error)
+{
+ return sev_issue_cmd(SEV_CMD_SHUTDOWN, 0, error);
+}
+EXPORT_SYMBOL_GPL(sev_platform_shutdown);
+
+int sev_platform_status(struct sev_data_status *data, int *error)
+{
+ return sev_issue_cmd(SEV_CMD_PLATFORM_STATUS, data, error);
+}
+EXPORT_SYMBOL_GPL(sev_platform_status);
+
+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_issue_cmd(cmd, data, error);
+}
+EXPORT_SYMBOL_GPL(sev_issue_cmd_external_user);
+
+int sev_guest_deactivate(struct sev_data_deactivate *data, int *error)
+{
+ return sev_issue_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_issue_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_issue_cmd(SEV_CMD_DECOMMISSION, data, error);
+}
+EXPORT_SYMBOL_GPL(sev_guest_decommission);
+
+int sev_guest_df_flush(int *error)
+{
+ return sev_issue_cmd(SEV_CMD_DF_FLUSH, 0, error);
+}
+EXPORT_SYMBOL_GPL(sev_guest_df_flush);
diff --git a/drivers/crypto/ccp/sev-dev.h b/drivers/crypto/ccp/sev-dev.h
new file mode 100644
index 0000000..0a8ce08
--- /dev/null
+++ b/drivers/crypto/ccp/sev-dev.h
@@ -0,0 +1,67 @@
+/*
+ * AMD Secure Encrypted Virtualization (SEV) interface
+ *
+ * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <[email protected]>
+ *
+ * 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 __SEV_DEV_H__
+#define __SEV_DEV_H__
+
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/miscdevice.h>
+
+#include <linux/psp-sev.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 MAX_PSP_NAME_LEN 16
+#define SEV_DEFAULT_TIMEOUT 5
+
+struct sev_device {
+ struct list_head entry;
+
+ struct dentry *debugfs;
+ struct miscdevice misc;
+
+ unsigned int id;
+ char name[MAX_PSP_NAME_LEN];
+
+ struct device *dev;
+ struct sp_device *sp;
+ struct psp_device *psp;
+
+ void __iomem *io_regs;
+
+ unsigned int int_rcvd;
+ wait_queue_head_t int_queue;
+};
+
+void sev_add_device(struct sev_device *sev);
+void sev_del_device(struct sev_device *sev);
+
+int sev_ops_init(struct sev_device *sev);
+void sev_ops_destroy(struct sev_device *sev);
+
+int sev_issue_cmd(int cmd, void *data, int *error);
+
+#endif /* __SEV_DEV_H */
diff --git a/drivers/crypto/ccp/sev-ops.c b/drivers/crypto/ccp/sev-ops.c
new file mode 100644
index 0000000..a13d857
--- /dev/null
+++ b/drivers/crypto/ccp/sev-ops.c
@@ -0,0 +1,457 @@
+/*
+ * AMD Secure Encrypted Virtualization (SEV) command interface
+ *
+ * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <[email protected]>
+ *
+ * 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/uaccess.h>
+
+#include <uapi/linux/psp-sev.h>
+
+#include "psp-dev.h"
+#include "sev-dev.h"
+
+static bool sev_initialized;
+static int sev_platform_get_state(int *state, int *error)
+{
+ int ret;
+ struct sev_data_status *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = sev_platform_status(data, error);
+ *state = data->state;
+
+ kfree(data);
+ return ret;
+}
+
+static int __sev_platform_init(int *error)
+{
+ int ret;
+ struct sev_data_init *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = sev_platform_init(data, error);
+
+ kfree(data);
+ return ret;
+}
+
+static int sev_ioctl_factory_reset(struct sev_issue_cmd *argp)
+{
+ return sev_issue_cmd(SEV_CMD_FACTORY_RESET, 0, &argp->error);
+}
+
+static int sev_ioctl_platform_status(struct sev_issue_cmd *argp)
+{
+ int ret;
+ struct sev_data_status *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = sev_platform_status(data, &argp->error);
+
+ if (copy_to_user((void *)argp->data, data, sizeof(*data)))
+ ret = -EFAULT;
+
+ kfree(data);
+ return ret;
+}
+
+static int sev_ioctl_pek_csr(struct sev_issue_cmd *argp)
+{
+ int do_shutdown = 0;
+ int ret, state, error;
+ void *csr_addr = NULL;
+ struct sev_data_pek_csr *data;
+ struct sev_user_data_pek_csr input;
+
+ if (copy_from_user(&input, (void *)argp->data,
+ sizeof(struct sev_user_data_pek_csr)))
+ return -EFAULT;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /*
+ * PEK_CSR command can be issued when firmware is in INIT or WORKING
+ * state. If firmware is in UNINIT state then we transition into INIT
+ * state and issue the command.
+ */
+ ret = sev_platform_get_state(&state, &argp->error);
+ if (ret)
+ return ret;
+
+ if (state == SEV_STATE_UNINIT) {
+ /* transition the plaform into INIT state */
+ ret = __sev_platform_init(&argp->error);
+ if (ret)
+ return ret;
+ do_shutdown = 1;
+ }
+
+ if (input.address) {
+ csr_addr = kmalloc(input.length, GFP_KERNEL);
+ if (!csr_addr) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+ data->address = __psp_pa(csr_addr);
+ data->length = input.length;
+ }
+
+ ret = sev_issue_cmd(SEV_CMD_PEK_CSR, data, &argp->error);
+
+ if (csr_addr) {
+ if (copy_to_user((void *)input.address, csr_addr,
+ input.length)) {
+ ret = -EFAULT;
+ goto e_free;
+ }
+ }
+
+ input.length = data->length;
+ if (copy_to_user((void *)argp->data, &input,
+ sizeof(struct sev_user_data_pek_csr)))
+ ret = -EFAULT;
+e_free:
+ if (do_shutdown)
+ sev_platform_shutdown(&error);
+ kfree(csr_addr);
+ kfree(data);
+ return ret;
+}
+
+static int sev_ioctl_pdh_gen(struct sev_issue_cmd *argp)
+{
+ int ret, state, error, do_shutdown = 0;
+
+ /*
+ * PDH_GEN command can be issued when platform is in INIT or WORKING
+ * state. If we are in UNINIT state then transition into INIT.
+ */
+ ret = sev_platform_get_state(&state, &argp->error);
+ if (ret)
+ return ret;
+
+ if (state == SEV_STATE_UNINIT) {
+ /* transition the plaform into INIT state */
+ ret = __sev_platform_init(&argp->error);
+ if (ret)
+ return ret;
+ do_shutdown = 1;
+ }
+
+ ret = sev_issue_cmd(SEV_CMD_PDH_GEN, 0, &argp->error);
+ if (do_shutdown)
+ sev_platform_shutdown(&error);
+ return ret;
+}
+
+static int sev_ioctl_pek_gen(struct sev_issue_cmd *argp)
+{
+ int do_shutdown = 0;
+ int error, ret, state;
+
+ /*
+ * PEK_GEN command can be issued only when firmware is in INIT state.
+ * If firmware is in UNINIT state then we transition into INIT state
+ * and issue the command and then shutdown.
+ */
+ ret = sev_platform_get_state(&state, &argp->error);
+ if (ret)
+ return ret;
+
+ if (state == SEV_STATE_UNINIT) {
+ /* transition the plaform into INIT state */
+ ret = __sev_platform_init(&argp->error);
+ if (ret)
+ return ret;
+
+ do_shutdown = 1;
+ }
+
+ ret = sev_issue_cmd(SEV_CMD_PEK_GEN, 0, &argp->error);
+
+ if (do_shutdown)
+ sev_platform_shutdown(&error);
+ return ret;
+}
+
+static int sev_ioctl_pek_cert_import(struct sev_issue_cmd *argp)
+{
+ int ret, state, error, do_shutdown = 0;
+ struct sev_data_pek_cert_import *data;
+ struct sev_user_data_pek_cert_import input;
+ void *pek_cert = NULL, *oca_cert = NULL;
+
+ if (copy_from_user(&input, (void *)argp->data, sizeof(*data)))
+ return -EFAULT;
+
+ if (!input.pek_cert_address || !input.pek_cert_length ||
+ !input.oca_cert_address || !input.oca_cert_length)
+ return -EINVAL;
+
+ ret = sev_platform_get_state(&state, &argp->error);
+ if (ret)
+ return ret;
+
+ /*
+ * CERT_IMPORT command can be issued only when platform is in INIT
+ * state. If we are in UNINIT state then transition into INIT state
+ * and issue the command.
+ */
+ if (state == SEV_STATE_UNINIT) {
+ /* transition platform init INIT state */
+ ret = __sev_platform_init(&argp->error);
+ if (ret)
+ return ret;
+ do_shutdown = 1;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+
+ pek_cert = kmalloc(input.pek_cert_length, GFP_KERNEL);
+ if (!pek_cert) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+
+ /* copy PEK certificate from userspace */
+ if (copy_from_user(pek_cert, (void *)input.pek_cert_address,
+ input.pek_cert_length)) {
+ ret = -EFAULT;
+ goto e_free;
+ }
+
+ data->pek_cert_address = __psp_pa(pek_cert);
+ data->pek_cert_length = input.pek_cert_length;
+
+ oca_cert = kmalloc(input.oca_cert_length, GFP_KERNEL);
+ if (!oca_cert) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+
+ /* copy OCA certificate from userspace */
+ if (copy_from_user(oca_cert, (void *)input.oca_cert_address,
+ input.oca_cert_length)) {
+ ret = -EFAULT;
+ goto e_free;
+ }
+
+ data->oca_cert_address = __psp_pa(oca_cert);
+ data->oca_cert_length = input.oca_cert_length;
+
+ ret = sev_issue_cmd(SEV_CMD_PEK_CERT_IMPORT, data, &argp->error);
+e_free:
+ if (do_shutdown)
+ sev_platform_shutdown(&error);
+ kfree(oca_cert);
+ kfree(pek_cert);
+ kfree(data);
+ return ret;
+}
+
+static int sev_ioctl_pdh_cert_export(struct sev_issue_cmd *argp)
+{
+ int ret, state, error, need_shutdown = 0;
+ struct sev_data_pdh_cert_export *data;
+ struct sev_user_data_pdh_cert_export input;
+ void *pdh_cert = NULL, *cert_chain = NULL;
+
+ if (copy_from_user(&input, (void *)argp->data, sizeof(*data)))
+ return -EFAULT;
+
+ /*
+ * CERT_EXPORT command can be issued in INIT or WORKING state.
+ * If we are in UNINIT state then transition into INIT state and
+ * shutdown before exiting. But if platform is in WORKING state
+ * then EXPORT the certificate but do not shutdown the platform.
+ */
+ ret = sev_platform_get_state(&state, &argp->error);
+ if (ret)
+ return ret;
+
+ if (state == SEV_STATE_UNINIT) {
+ ret = __sev_platform_init(&argp->error);
+ if (ret)
+ return ret;
+ need_shutdown = 1;
+ }
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+
+ if (input.pdh_cert_address) {
+ pdh_cert = kmalloc(input.pdh_cert_length, GFP_KERNEL);
+ if (!pdh_cert) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+
+ data->pdh_cert_address = __psp_pa(pdh_cert);
+ data->pdh_cert_length = input.pdh_cert_length;
+ }
+
+ if (input.cert_chain_address) {
+ cert_chain = kmalloc(input.cert_chain_length, GFP_KERNEL);
+ if (!cert_chain) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+
+ data->cert_chain_address = __psp_pa(cert_chain);
+ data->cert_chain_length = input.cert_chain_length;
+ }
+
+ ret = sev_issue_cmd(SEV_CMD_PDH_CERT_EXPORT, data, &argp->error);
+
+ input.cert_chain_length = data->cert_chain_length;
+ input.pdh_cert_length = data->pdh_cert_length;
+
+ /* copy PDH certificate to userspace */
+ if (pdh_cert) {
+ if (copy_to_user((void *)input.pdh_cert_address,
+ pdh_cert, input.pdh_cert_length)) {
+ ret = -EFAULT;
+ goto e_free;
+ }
+ }
+
+ /* copy certificate chain to userspace */
+ if (cert_chain) {
+ if (copy_to_user((void *)input.cert_chain_address,
+ cert_chain, input.cert_chain_length)) {
+ ret = -EFAULT;
+ goto e_free;
+ }
+ }
+
+ /* copy certificate length to userspace */
+ if (copy_to_user((void *)argp->data, &input,
+ sizeof(struct sev_user_data_pdh_cert_export)))
+ ret = -EFAULT;
+
+e_free:
+ if (need_shutdown)
+ sev_platform_shutdown(&error);
+
+ kfree(cert_chain);
+ kfree(pdh_cert);
+ kfree(data);
+ return ret;
+}
+
+static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
+{
+ int ret = -EFAULT;
+ void __user *argp = (void __user *)arg;
+ struct sev_issue_cmd input;
+
+ if (ioctl != SEV_ISSUE_CMD)
+ return -EINVAL;
+
+ if (copy_from_user(&input, argp, sizeof(struct sev_issue_cmd)))
+ return -EFAULT;
+
+ if (input.cmd > SEV_CMD_MAX)
+ return -EINVAL;
+
+ switch (input.cmd) {
+
+ case SEV_USER_CMD_FACTORY_RESET: {
+ ret = sev_ioctl_factory_reset(&input);
+ break;
+ }
+ case SEV_USER_CMD_PLATFORM_STATUS: {
+ ret = sev_ioctl_platform_status(&input);
+ break;
+ }
+ case SEV_USER_CMD_PEK_GEN: {
+ ret = sev_ioctl_pek_gen(&input);
+ break;
+ }
+ case SEV_USER_CMD_PDH_GEN: {
+ ret = sev_ioctl_pdh_gen(&input);
+ break;
+ }
+ case SEV_USER_CMD_PEK_CSR: {
+ ret = sev_ioctl_pek_csr(&input);
+ break;
+ }
+ case SEV_USER_CMD_PEK_CERT_IMPORT: {
+ ret = sev_ioctl_pek_cert_import(&input);
+ break;
+ }
+ case SEV_USER_CMD_PDH_CERT_EXPORT: {
+ ret = sev_ioctl_pdh_cert_export(&input);
+ break;
+ }
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ if (copy_to_user(argp, &input, sizeof(struct sev_issue_cmd)))
+ ret = -EFAULT;
+
+ return ret;
+}
+
+const struct file_operations sev_fops = {
+ .owner = THIS_MODULE,
+ .unlocked_ioctl = sev_ioctl,
+};
+
+int sev_ops_init(struct sev_device *sev)
+{
+ struct miscdevice *misc = &sev->misc;
+
+ /* if sev device is already registered then do nothing */
+ if (sev_initialized)
+ return 0;
+
+ misc->minor = MISC_DYNAMIC_MINOR;
+ misc->name = sev->name;
+ misc->fops = &sev_fops;
+ sev_initialized = true;
+
+ return misc_register(misc);
+}
+
+void sev_ops_destroy(struct sev_device *sev)
+{
+ misc_deregister(&sev->misc);
+}
diff --git a/drivers/crypto/ccp/sp-pci.c b/drivers/crypto/ccp/sp-pci.c
index e58b3ad..20a0f35 100644
--- a/drivers/crypto/ccp/sp-pci.c
+++ b/drivers/crypto/ccp/sp-pci.c
@@ -280,7 +280,7 @@ static const struct sp_dev_vdata dev_vdata[] = {
#ifdef CONFIG_CRYPTO_DEV_SP_CCP
.ccp_vdata = &ccpv5a,
#endif
-#ifdef CONFIG_CRYPTO_DEV_PSP
+#ifdef CONFIG_CRYPTO_DEV_SP_PSP
.psp_vdata = &psp_entry
#endif
},
diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
new file mode 100644
index 0000000..1736880
--- /dev/null
+++ b/include/linux/psp-sev.h
@@ -0,0 +1,683 @@
+/*
+ * AMD Secure Encrypted Virtualization (SEV) driver interface
+ *
+ * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <[email protected]>
+ *
+ * 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_SEV_H__
+#define __PSP_SEV_H__
+
+#ifdef CONFIG_X86
+#include <linux/mem_encrypt.h>
+
+#define __psp_pa(x) __sme_pa(x)
+#else
+#define __psp_pa(x) __pa(x)
+#endif
+
+/**
+ * SEV platform state
+ */
+enum sev_state {
+ SEV_STATE_UNINIT = 0x0,
+ SEV_STATE_INIT = 0x1,
+ SEV_STATE_WORKING = 0x2,
+
+ SEV_STATE_MAX
+};
+
+/**
+ * SEV platform and guest management commands
+ */
+enum sev_cmd {
+ /* platform commands */
+ SEV_CMD_INIT = 0x001,
+ SEV_CMD_SHUTDOWN = 0x002,
+ SEV_CMD_FACTORY_RESET = 0x003,
+ SEV_CMD_PLATFORM_STATUS = 0x004,
+ SEV_CMD_PEK_GEN = 0x005,
+ SEV_CMD_PEK_CSR = 0x006,
+ SEV_CMD_PEK_CERT_IMPORT = 0x007,
+ SEV_CMD_PDH_CERT_EXPORT = 0x008,
+ SEV_CMD_PDH_GEN = 0x009,
+ SEV_CMD_DF_FLUSH = 0x00A,
+
+ /* Guest commands */
+ SEV_CMD_DECOMMISSION = 0x020,
+ SEV_CMD_ACTIVATE = 0x021,
+ SEV_CMD_DEACTIVATE = 0x022,
+ SEV_CMD_GUEST_STATUS = 0x023,
+
+ /* Guest launch commands */
+ SEV_CMD_LAUNCH_START = 0x030,
+ SEV_CMD_LAUNCH_UPDATE_DATA = 0x031,
+ SEV_CMD_LAUNCH_UPDATE_VMSA = 0x032,
+ SEV_CMD_LAUNCH_MEASURE = 0x033,
+ SEV_CMD_LAUNCH_UPDATE_SECRET = 0x034,
+ SEV_CMD_LAUNCH_FINISH = 0x035,
+
+ /* Guest migration commands (outgoing) */
+ SEV_CMD_SEND_START = 0x040,
+ SEV_CMD_SEND_UPDATE_DATA = 0x041,
+ SEV_CMD_SEND_UPDATE_VMSA = 0x042,
+ SEV_CMD_SEND_FINISH = 0x043,
+
+ /* Guest migration commands (incoming) */
+ SEV_CMD_RECEIVE_START = 0x050,
+ SEV_CMD_RECEIVE_UPDATE_DATA = 0x051,
+ SEV_CMD_RECEIVE_UPDATE_VMSA = 0x052,
+ SEV_CMD_RECEIVE_FINISH = 0x053,
+
+ /* Guest debug commands */
+ SEV_CMD_DBG_DECRYPT = 0x060,
+ SEV_CMD_DBG_ENCRYPT = 0x061,
+
+ SEV_CMD_MAX,
+};
+
+/**
+ * status code returned by the commands
+ */
+enum psp_ret_code {
+ SEV_RET_SUCCESS = 0,
+ SEV_RET_INVALID_PLATFORM_STATE,
+ SEV_RET_INVALID_GUEST_STATE,
+ SEV_RET_INAVLID_CONFIG,
+ SEV_RET_INVALID_LENGTH,
+ SEV_RET_ALREADY_OWNED,
+ SEV_RET_INVALID_CERTIFICATE,
+ SEV_RET_POLICY_FAILURE,
+ SEV_RET_INACTIVE,
+ SEV_RET_INVALID_ADDRESS,
+ SEV_RET_BAD_SIGNATURE,
+ SEV_RET_BAD_MEASUREMENT,
+ SEV_RET_ASID_OWNED,
+ SEV_RET_INVALID_ASID,
+ SEV_RET_WBINVD_REQUIRED,
+ SEV_RET_DFFLUSH_REQUIRED,
+ SEV_RET_INVALID_GUEST,
+ SEV_RET_INVALID_COMMAND,
+ SEV_RET_ACTIVE,
+ SEV_RET_HWSEV_RET_PLATFORM,
+ SEV_RET_HWSEV_RET_UNSAFE,
+ SEV_RET_UNSUPPORTED,
+ SEV_RET_MAX,
+};
+
+/**
+ * struct sev_data_init - INIT command parameters
+ *
+ * @flags: processing flags
+ * @tmr_address: system physical address used for SEV-ES
+ * @tmr_length: length of tmr_address
+ */
+struct sev_data_init {
+ __u32 flags; /* In */
+ __u32 reserved; /* In */
+ __u64 tmr_address; /* In */
+ __u32 tmr_length; /* In */
+};
+
+/**
+ * struct sev_data_status - PLATFORM_STATUS command parameters
+ *
+ * @major: major API version
+ * @minor: minor API version
+ * @state: platform state
+ * @owner: self-owned or externally owned
+ * @config: platform config flags
+ * @guest_count: number of active guests
+ */
+struct sev_data_status {
+ __u8 api_major; /* Out */
+ __u8 api_minor; /* Out */
+ __u8 state; /* Out */
+ __u8 owner; /* Out */
+ __u32 config; /* Out */
+ __u32 guest_count; /* Out */
+};
+
+/**
+ * struct sev_data_pek_csr - PEK_CSR command parameters
+ *
+ * @address: PEK certificate chain
+ * @length: length of certificate
+ */
+struct sev_data_pek_csr {
+ __u64 address; /* In */
+ __u32 length; /* In/Out */
+};
+
+/**
+ * struct sev_data_cert_import - PEK_CERT_IMPORT command parameters
+ *
+ * @pek_address: PEK certificate chain
+ * @pek_length: length of PEK certificate
+ * @oca_address: OCA certificate chain
+ * @oca_length: length of OCA certificate
+ */
+struct sev_data_pek_cert_import {
+ __u64 pek_cert_address; /* In */
+ __u32 pek_cert_length; /* In */
+ __u32 reserved; /* In */
+ __u64 oca_cert_address; /* In */
+ __u32 oca_cert_length; /* In */
+};
+
+/**
+ * struct sev_data_pdh_cert_export - PDH_CERT_EXPORT command parameters
+ *
+ * @pdh_address: PDH certificate address
+ * @pdh_length: length of PDH certificate
+ * @cert_chain_address: PDH certificate chain
+ * @cert_chain_length: length of PDH certificate chain
+ */
+struct sev_data_pdh_cert_export {
+ __u64 pdh_cert_address; /* In */
+ __u32 pdh_cert_length; /* In/Out */
+ __u32 reserved; /* In */
+ __u64 cert_chain_address; /* In */
+ __u32 cert_chain_length; /* In/Out */
+};
+
+/**
+ * struct sev_data_decommission - DECOMMISSION command parameters
+ *
+ * @handle: handle of the VM to decommission
+ */
+struct sev_data_decommission {
+ u32 handle; /* In */
+};
+
+/**
+ * struct sev_data_activate - ACTIVATE command parameters
+ *
+ * @handle: handle of the VM to activate
+ * @asid: asid assigned to the VM
+ */
+struct sev_data_activate {
+ u32 handle; /* In */
+ u32 asid; /* In */
+};
+
+/**
+ * struct sev_data_deactivate - DEACTIVATE command parameters
+ *
+ * @handle: handle of the VM to deactivate
+ */
+struct sev_data_deactivate {
+ u32 handle; /* In */
+};
+
+/**
+ * struct sev_data_guest_status - SEV GUEST_STATUS command parameters
+ *
+ * @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 sev_data_guest_status {
+ u32 handle; /* In */
+ u32 policy; /* Out */
+ u32 asid; /* Out */
+ u8 state; /* Out */
+};
+
+/**
+ * struct sev_data_launch_start - LAUNCH_START command parameters
+ *
+ * @handle: handle assigned to the VM
+ * @policy: guest launch policy
+ * @dh_cert_address: physical address of DH certificate blob
+ * @dh_cert_length: length of DH certificate blob
+ * @session_address: physical address of session parameters
+ * @session_len: length of session parameters
+ */
+struct sev_data_launch_start {
+ u32 handle; /* In/Out */
+ u32 policy; /* In */
+ u64 dh_cert_address; /* In */
+ u32 dh_cert_length; /* In */
+ u32 reserved; /* In */
+ u64 session_address; /* In */
+ u32 session_length; /* In */
+};
+
+/**
+ * struct sev_data_launch_update_data - LAUNCH_UPDATE_DATA command parameter
+ *
+ * @handle: handle of the VM to update
+ * @length: length of memory to be encrypted
+ * @address: physical address of memory region to encrypt
+ */
+struct sev_data_launch_update_data {
+ u32 handle; /* In */
+ u32 reserved;
+ u64 address; /* In */
+ u32 length; /* In */
+};
+
+/**
+ * struct sev_data_launch_update_vmsa - LAUNCH_UPDATE_VMSA command
+ *
+ * @handle: handle of the VM
+ * @address: physical address of memory region to encrypt
+ * @length: length of memory region to encrypt
+ */
+struct sev_data_launch_update_vmsa {
+ u32 handle; /* In */
+ u32 reserved;
+ u64 address; /* In */
+ u32 length; /* In */
+};
+
+/**
+ * struct sev_data_launch_measure - LAUNCH_MEASURE command parameters
+ *
+ * @handle: handle of the VM to process
+ * @address: physical address containing the measurement blob
+ * @length: length of measurement blob
+ */
+struct sev_data_launch_measure {
+ u32 handle; /* In */
+ u32 reserved;
+ u64 address; /* In */
+ u32 length; /* In/Out */
+};
+
+/**
+ * struct sev_data_launch_secret - LAUNCH_SECRET command parameters
+ *
+ * @handle: handle of the VM to process
+ * @hdr_address: physical address containing the packet header
+ * @hdr_length: length of packet header
+ * @guest_address: system physical address of guest memory region
+ * @guest_length: length of guest_paddr
+ * @trans_address: physical address of transport memory buffer
+ * @trans_length: length of transport memory buffer
+ */
+struct sev_data_launch_secret {
+ u32 handle; /* In */
+ u32 reserved1;
+ u64 hdr_address; /* In */
+ u32 hdr_length; /* In */
+ u32 reserved2;
+ u64 guest_address; /* In */
+ u32 guest_length; /* In */
+ u32 reserved3;
+ u64 trans_address; /* In */
+ u32 trans_length; /* In */
+};
+
+/**
+ * struct sev_data_launch_finish - LAUNCH_FINISH command parameters
+ *
+ * @handle: handle of the VM to process
+ */
+struct sev_data_launch_finish {
+ u32 handle; /* In */
+};
+
+/**
+ * struct sev_data_send_start - SEND_START command parameters
+ *
+ * @handle: handle of the VM to process
+ * @policy: policy information for the VM
+ * @pdh_cert_address: physical address containing PDH certificate
+ * @pdh_cert_length: length of PDH certificate
+ * @plat_certs_address: physical address containing platform certificate
+ * @plat_certs_length: length of platform certificate
+ * @amd_certs_address: physical address containing AMD certificate
+ * @amd_certs_length: length of AMD certificate
+ * @session_address: physical address containing Session data
+ * @session_length: length of session data
+ */
+struct sev_data_send_start {
+ u32 handle; /* In */
+ u32 policy; /* Out */
+ u64 pdh_cert_address; /* In */
+ u32 pdh_cert_length; /* In */
+ u32 reserved1;
+ u64 plat_cert_address; /* In */
+ u32 plat_cert_length; /* In */
+ u32 reserved2;
+ u64 amd_cert_address; /* In */
+ u32 amd_cert_length; /* In */
+ u32 reserved3;
+ u64 session_address; /* In */
+ u32 session_length; /* In/Out */
+};
+
+/**
+ * struct sev_data_send_update - SEND_UPDATE_DATA command
+ *
+ * @handle: handle of the VM to process
+ * @hdr_address: physical address containing packet header
+ * @hdr_length: length of packet header
+ * @guest_address: physical address of guest memory region to send
+ * @guest_length: length of guest memory region to send
+ * @trans_address: physical address of host memory region
+ * @trans_length: length of host memory region
+ */
+struct sev_data_send_update_data {
+ u32 handle; /* In */
+ u32 reserved1;
+ u64 hdr_address; /* In */
+ u32 hdr_length; /* In/Out */
+ u32 reserved2;
+ u64 guest_address; /* In */
+ u32 guest_length; /* In */
+ u32 reserved3;
+ u64 trans_address; /* In */
+ u32 trans_length; /* In */
+};
+
+/**
+ * struct sev_data_send_update - SEND_UPDATE_VMSA command
+ *
+ * @handle: handle of the VM to process
+ * @hdr_address: physical address containing packet header
+ * @hdr_length: length of packet header
+ * @guest_address: physical address of guest memory region to send
+ * @guest_length: length of guest memory region to send
+ * @trans_address: physical address of host memory region
+ * @trans_length: length of host memory region
+ */
+struct sev_data_send_update_vmsa {
+ u32 handle; /* In */
+ u64 hdr_address; /* In */
+ u32 hdr_length; /* In/Out */
+ u32 reserved2;
+ u64 guest_address; /* In */
+ u32 guest_length; /* In */
+ u32 reserved3;
+ u64 trans_address; /* In */
+ u32 trans_length; /* In */
+};
+
+/**
+ * struct sev_data_send_finish - SEND_FINISH command parameters
+ *
+ * @handle: handle of the VM to process
+ */
+struct sev_data_send_finish {
+ u32 handle; /* In */
+};
+
+/**
+ * struct sev_data_receive_start - RECEIVE_START command parameters
+ *
+ * @handle: handle of the VM to perform receive operation
+ * @pdh_cert_address: system physical address containing PDH certificate blob
+ * @pdh_cert_length: length of PDH certificate blob
+ * @session_address: system physical address containing session blob
+ * @session_length: length of session blob
+ */
+struct sev_data_receive_start {
+ u32 handle; /* In/Out */
+ u32 policy; /* In */
+ u64 pdh_cert_address; /* In */
+ u32 pdh_cert_length; /* In */
+ u32 reserved1;
+ u64 session_address; /* In */
+ u32 session_length; /* In */
+};
+
+/**
+ * struct sev_data_receive_update_data - RECEIVE_UPDATE_DATA command parameters
+ *
+ * @handle: handle of the VM to update
+ * @hdr_address: physical address containing packet header blob
+ * @hdr_length: length of packet header
+ * @guest_address: system physical address of guest memory region
+ * @guest_length: length of guest memory region
+ * @trans_address: system physical address of transport buffer
+ * @trans_length: length of transport buffer
+ */
+struct sev_data_receive_update_data {
+ u32 handle; /* In */
+ u32 reserved1;
+ u64 hdr_address; /* In */
+ u32 hdr_length; /* In */
+ u32 reserved2;
+ u64 guest_address; /* In */
+ u32 guest_length; /* In */
+ u32 reserved3;
+ u64 trans_address; /* In */
+ u32 trans_length; /* In */
+};
+
+/**
+ * struct sev_data_receive_update_vmsa - RECEIVE_UPDATE_VMSA command parameters
+ *
+ * @handle: handle of the VM to update
+ * @hdr_address: physical address containing packet header blob
+ * @hdr_length: length of packet header
+ * @guest_address: system physical address of guest memory region
+ * @guest_length: length of guest memory region
+ * @trans_address: system physical address of transport buffer
+ * @trans_length: length of transport buffer
+ */
+struct sev_data_receive_update_vmsa {
+ u32 handle; /* In */
+ u32 reserved1;
+ u64 hdr_address; /* In */
+ u32 hdr_length; /* In */
+ u32 reserved2;
+ u64 guest_address; /* In */
+ u32 guest_length; /* In */
+ u32 reserved3;
+ u64 trans_address; /* In */
+ u32 trans_length; /* In */
+};
+
+/**
+ * struct sev_data_receive_finish - RECEIVE_FINISH command parameters
+ *
+ * @handle: handle of the VM to finish
+ */
+struct sev_data_receive_finish {
+ u32 handle; /* In */
+};
+
+/**
+ * struct sev_data_dbg - DBG_ENCRYPT/DBG_DECRYPT command parameters
+ *
+ * @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 sev_data_dbg {
+ u32 handle; /* In */
+ u32 reserved;
+ u64 src_addr; /* In */
+ u64 dst_addr; /* In */
+ u32 length; /* In */
+};
+
+#if defined(CONFIG_CRYPTO_DEV_PSP_SEV)
+
+/**
+ * sev_platform_init - perform SEV INIT command
+ *
+ * @init: sev_data_init structure to be processed
+ * @error: SEV command return code
+ *
+ * Returns:
+ * 0 if the SEV successfully processed the command
+ * -%ENODEV if the SEV device is not available
+ * -%ENOTSUPP if the SEV does not support SEV
+ * -%ETIMEDOUT if the SEV command timed out
+ * -%EIO if the SEV returned a non-zero return code
+ */
+int sev_platform_init(struct sev_data_init *init, int *error);
+
+/**
+ * sev_platform_shutdown - perform SEV SHUTDOWN command
+ *
+ * @error: SEV command return code
+ *
+ * Returns:
+ * 0 if the SEV successfully processed the command
+ * -%ENODEV if the SEV device is not available
+ * -%ENOTSUPP if the SEV does not support SEV
+ * -%ETIMEDOUT if the SEV command timed out
+ * -%EIO if the SEV returned a non-zero return code
+ */
+int sev_platform_shutdown(int *error);
+
+/**
+ * sev_platform_status - perform SEV PLATFORM_STATUS command
+ *
+ * @init: sev_data_status structure to be processed
+ * @error: SEV command return code
+ *
+ * Returns:
+ * 0 if the SEV successfully processed the command
+ * -%ENODEV if the SEV device is not available
+ * -%ENOTSUPP if the SEV does not support SEV
+ * -%ETIMEDOUT if the SEV command timed out
+ * -%EIO if the SEV returned a non-zero return code
+ */
+int sev_platform_status(struct sev_data_status *status, int *error);
+
+/**
+ * sev_issue_cmd_external_user - issue SEV command by other driver
+ *
+ * The function can be used by other drivers to issue a SEV command on
+ * behalf by userspace. The caller must pass a valid SEV file descriptor
+ * so that we know that caller has access to SEV device.
+ *
+ * @filep - SEV device file pointer
+ * @cmd - command to issue
+ * @data - command buffer
+ * @error: SEV command return code
+ *
+ * Returns:
+ * 0 if the SEV successfully processed the command
+ * -%ENODEV if the SEV device is not available
+ * -%ENOTSUPP if the SEV does not support SEV
+ * -%ETIMEDOUT if the SEV command timed out
+ * -%EIO if the SEV returned a non-zero return code
+ * -%EINVAL if the SEV file descriptor is not valid
+ */
+int sev_issue_cmd_external_user(struct file *filep, unsigned int id,
+ void *data, int *error);
+
+/**
+ * sev_guest_deactivate - perform SEV DEACTIVATE command
+ *
+ * @deactivate: sev_data_deactivate structure to be processed
+ * @sev_ret: sev command return code
+ *
+ * Returns:
+ * 0 if the sev successfully processed the command
+ * -%ENODEV if the sev device is not available
+ * -%ENOTSUPP if the sev does not support SEV
+ * -%ETIMEDOUT if the sev command timed out
+ * -%EIO if the sev returned a non-zero return code
+ */
+int sev_guest_deactivate(struct sev_data_deactivate *data, int *error);
+
+/**
+ * sev_guest_activate - perform SEV ACTIVATE command
+ *
+ * @activate: sev_data_activate structure to be processed
+ * @sev_ret: sev command return code
+ *
+ * Returns:
+ * 0 if the sev successfully processed the command
+ * -%ENODEV if the sev device is not available
+ * -%ENOTSUPP if the sev does not support SEV
+ * -%ETIMEDOUT if the sev command timed out
+ * -%EIO if the sev returned a non-zero return code
+ */
+int sev_guest_activate(struct sev_data_activate *data, int *error);
+
+/**
+ * sev_guest_df_flush - perform SEV DF_FLUSH command
+ *
+ * @sev_ret: sev command return code
+ *
+ * Returns:
+ * 0 if the sev successfully processed the command
+ * -%ENODEV if the sev device is not available
+ * -%ENOTSUPP if the sev does not support SEV
+ * -%ETIMEDOUT if the sev command timed out
+ * -%EIO if the sev returned a non-zero return code
+ */
+int sev_guest_df_flush(int *error);
+
+/**
+ * sev_guest_decommission - perform SEV DECOMMISSION command
+ *
+ * @decommission: sev_data_decommission structure to be processed
+ * @sev_ret: sev command return code
+ *
+ * Returns:
+ * 0 if the sev successfully processed the command
+ * -%ENODEV if the sev device is not available
+ * -%ENOTSUPP if the sev does not support SEV
+ * -%ETIMEDOUT if the sev command timed out
+ * -%EIO if the sev returned a non-zero return code
+ */
+int sev_guest_decommission(struct sev_data_decommission *data, int *error);
+
+#else /* !CONFIG_CRYPTO_DEV_PSP_SEV */
+
+static inline int sev_platform_status(struct sev_data_status *status,
+ int *error)
+{
+ return -ENODEV;
+}
+
+static inline int sev_platform_init(struct sev_data_init *init, int *error)
+{
+ return -ENODEV;
+}
+
+static inline int sev_platform_shutdown(int *error)
+{
+ return -ENODEV;
+}
+
+static inline int sev_issue_cmd_external_user(int fd, unsigned int id,
+ void *data, int *error)
+{
+ return -ENODEV;
+}
+
+static inline int sev_guest_deactivate(struct sev_data_deactivate *data,
+ int *error)
+{
+ return -ENODEV;
+}
+
+static inline int sev_guest_decommission(struct sev_data_decommission *data,
+ int *error)
+{
+ return -ENODEV;
+}
+
+static inline int sev_guest_activate(struct sev_data_activate *data,
+ int *error)
+{
+ return -ENODEV;
+}
+
+static inline int sev_guest_df_flush(int *error)
+{
+ return -ENODEV;
+}
+
+#endif /* CONFIG_CRYPTO_DEV_PSP_SEV */
+
+#endif /* __PSP_SEV_H__ */
diff --git a/include/uapi/linux/psp-sev.h b/include/uapi/linux/psp-sev.h
new file mode 100644
index 0000000..eb802f8
--- /dev/null
+++ b/include/uapi/linux/psp-sev.h
@@ -0,0 +1,110 @@
+
+/*
+ * Userspace interface for AMD Secure Encrypted Virtualization (SEV)
+ *
+ * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <[email protected]>
+ *
+ * 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_SEV_USER_H__
+#define __PSP_SEV_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * SEV platform commands
+ */
+enum {
+ SEV_USER_CMD_FACTORY_RESET = 0,
+ SEV_USER_CMD_PLATFORM_STATUS,
+ SEV_USER_CMD_PEK_GEN,
+ SEV_USER_CMD_PEK_CSR,
+ SEV_USER_CMD_PDH_GEN,
+ SEV_USER_CMD_PDH_CERT_EXPORT,
+ SEV_USER_CMD_PEK_CERT_IMPORT,
+
+ SEV_USER_CMD_MAX,
+};
+
+/**
+ * struct sev_user_data_status - PLATFORM_STATUS command parameters
+ *
+ * @major: major API version
+ * @minor: minor API version
+ * @state: platform state
+ * @owner: self-owned or externally owned
+ * @config: platform config flags
+ * @guest_count: number of active guests
+ */
+struct sev_user_data_status {
+ __u8 api_major; /* Out */
+ __u8 api_minor; /* Out */
+ __u8 state; /* Out */
+ __u8 owner; /* Out */
+ __u32 config; /* Out */
+ __u32 guest_count; /* Out */
+};
+
+/**
+ * struct sev_user_data_pek_csr - PEK_CSR command parameters
+ *
+ * @address: PEK certificate chain
+ * @length: length of certificate
+ */
+struct sev_user_data_pek_csr {
+ __u64 address; /* In */
+ __u32 length; /* In/Out */
+};
+
+/**
+ * struct sev_user_data_cert_import - PEK_CERT_IMPORT command parameters
+ *
+ * @pek_address: PEK certificate chain
+ * @pek_length: length of PEK certificate
+ * @oca_address: OCA certificate chain
+ * @oca_length: length of OCA certificate
+ */
+struct sev_user_data_pek_cert_import {
+ __u64 pek_cert_address; /* In */
+ __u32 pek_cert_length; /* In */
+ __u64 oca_cert_address; /* In */
+ __u32 oca_cert_length; /* In */
+};
+
+/**
+ * struct sev_user_data_pdh_cert_export - PDH_CERT_EXPORT command parameters
+ *
+ * @pdh_address: PDH certificate address
+ * @pdh_length: length of PDH certificate
+ * @cert_chain_address: PDH certificate chain
+ * @cert_chain_length: length of PDH certificate chain
+ */
+struct sev_user_data_pdh_cert_export {
+ __u64 pdh_cert_address; /* In */
+ __u32 pdh_cert_length; /* In/Out */
+ __u64 cert_chain_address; /* In */
+ __u32 cert_chain_length; /* In/Out */
+};
+
+/**
+ * struct sev_issue_cmd - SEV ioctl parameters
+ *
+ * @cmd: SEV commands to execute
+ * @opaque: pointer to the command structure
+ * @error: SEV FW return code on failure
+ */
+struct sev_issue_cmd {
+ __u32 cmd; /* In */
+ __u64 data; /* In */
+ __u32 error; /* Out */
+};
+
+#define SEV_IOC_TYPE 'S'
+#define SEV_ISSUE_CMD _IOWR(SEV_IOC_TYPE, 0x0, struct sev_issue_cmd)
+
+#endif /* __PSP_USER_SEV_H */
--
2.9.4
From: Tom Lendacky <[email protected]>
Currently the nested_ctl variable in the vmcb_control_area structure is
used to indicate nested paging support. The nested paging support field
is actually defined as bit 0 of the field. In order to support a new
feature flag the usage of the nested_ctl and nested paging support must
be converted to operate on a single bit.
Signed-off-by: Tom Lendacky <[email protected]>
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/include/asm/svm.h | 2 ++
arch/x86/kvm/svm.c | 7 ++++---
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index 58fffe7..a3d9e0b 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -139,6 +139,8 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
#define SVM_VM_CR_SVM_LOCK_MASK 0x0008ULL
#define SVM_VM_CR_SVM_DIS_MASK 0x0010ULL
+#define SVM_NESTED_CTL_NP_ENABLE BIT(0)
+
struct __attribute__ ((__packed__)) vmcb_seg {
u16 selector;
u16 attrib;
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 06bd902..1cd7c78 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -1296,7 +1296,7 @@ static void init_vmcb(struct vcpu_svm *svm)
if (npt_enabled) {
/* Setup VMCB for Nested Paging */
- control->nested_ctl = 1;
+ control->nested_ctl |= SVM_NESTED_CTL_NP_ENABLE;
clr_intercept(svm, INTERCEPT_INVLPG);
clr_exception_intercept(svm, PF_VECTOR);
clr_cr_intercept(svm, INTERCEPT_CR3_READ);
@@ -2904,7 +2904,8 @@ static bool nested_vmcb_checks(struct vmcb *vmcb)
if (vmcb->control.asid == 0)
return false;
- if (vmcb->control.nested_ctl && !npt_enabled)
+ if ((vmcb->control.nested_ctl & SVM_NESTED_CTL_NP_ENABLE) &&
+ !npt_enabled)
return false;
return true;
@@ -2979,7 +2980,7 @@ static bool nested_svm_vmrun(struct vcpu_svm *svm)
else
svm->vcpu.arch.hflags &= ~HF_HIF_MASK;
- if (nested_vmcb->control.nested_ctl) {
+ if (nested_vmcb->control.nested_ctl & SVM_NESTED_CTL_NP_ENABLE) {
kvm_mmu_unload(&svm->vcpu);
svm->nested.nested_cr3 = nested_vmcb->control.nested_cr3;
nested_svm_init_mmu_context(&svm->vcpu);
--
2.9.4
In current implementation, asid allocation starts from 1, this patch
adds a min_asid variable in svm_vcpu structure to allow starting asid
from something other than 1.
Signed-off-by: Brijesh Singh <[email protected]>
Reviewed-by: Paolo Bonzini <[email protected]>
---
arch/x86/kvm/svm.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 6af04dd..46f41bb 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -511,6 +511,7 @@ struct svm_cpu_data {
u64 asid_generation;
u32 max_asid;
u32 next_asid;
+ u32 min_asid;
struct kvm_ldttss_desc *tss_desc;
struct page *save_area;
@@ -768,6 +769,7 @@ static int svm_hardware_enable(void)
sd->asid_generation = 1;
sd->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1;
sd->next_asid = sd->max_asid + 1;
+ sd->min_asid = 1;
gdt = get_current_gdt_rw();
sd->tss_desc = (struct kvm_ldttss_desc *)(gdt + GDT_ENTRY_TSS);
@@ -2072,7 +2074,7 @@ static void new_asid(struct vcpu_svm *svm, struct svm_cpu_data *sd)
{
if (sd->next_asid > sd->max_asid) {
++sd->asid_generation;
- sd->next_asid = 1;
+ sd->next_asid = sd->min_asid;
svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID;
}
--
2.9.4
If hardware supports encrypting then KVM_MEMORY_ENCRYPT_OP ioctl can
be used by qemu to issue platform specific memory encryption commands.
Signed-off-by: Brijesh Singh <[email protected]>
Reviewed-by: Paolo Bonzini <[email protected]>
---
arch/x86/include/asm/kvm_host.h | 2 ++
arch/x86/kvm/x86.c | 12 ++++++++++++
include/uapi/linux/kvm.h | 2 ++
3 files changed, 16 insertions(+)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 7cbaab5..99a0e11 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1057,6 +1057,8 @@ struct kvm_x86_ops {
void (*cancel_hv_timer)(struct kvm_vcpu *vcpu);
void (*setup_mce)(struct kvm_vcpu *vcpu);
+
+ int (*memory_encryption_op)(struct kvm *kvm, void __user *argp);
};
struct kvm_arch_async_pf {
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 88be1aa..c9d3ff5 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3974,6 +3974,14 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm,
return r;
}
+static int kvm_vm_ioctl_memory_encryption_op(struct kvm *kvm, void __user *argp)
+{
+ if (kvm_x86_ops->memory_encryption_op)
+ return kvm_x86_ops->memory_encryption_op(kvm, argp);
+
+ return -ENOTTY;
+}
+
long kvm_arch_vm_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@@ -4234,6 +4242,10 @@ long kvm_arch_vm_ioctl(struct file *filp,
r = kvm_vm_ioctl_enable_cap(kvm, &cap);
break;
}
+ case KVM_MEMORY_ENCRYPT_OP: {
+ r = kvm_vm_ioctl_memory_encryption_op(kvm, argp);
+ break;
+ }
default:
r = -ENOTTY;
}
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 6cd63c1..ab3b711 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1355,6 +1355,8 @@ struct kvm_s390_ucas_mapping {
/* Available with KVM_CAP_S390_CMMA_MIGRATION */
#define KVM_S390_GET_CMMA_BITS _IOWR(KVMIO, 0xb8, struct kvm_s390_cmma_log)
#define KVM_S390_SET_CMMA_BITS _IOW(KVMIO, 0xb9, struct kvm_s390_cmma_log)
+/* Memory Encryption Commands */
+#define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xba, unsigned long)
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
--
2.9.4
From: Tom Lendacky <[email protected]>
Define the SEV enable bit for the VMCB control structure. The hypervisor
will use this bit to enable SEV in the guest.
Signed-off-by: Tom Lendacky <[email protected]>
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/include/asm/svm.h | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index a3d9e0b..0be01f9 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -140,6 +140,7 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
#define SVM_VM_CR_SVM_DIS_MASK 0x0010ULL
#define SVM_NESTED_CTL_NP_ENABLE BIT(0)
+#define SVM_NESTED_CTL_SEV_ENABLE BIT(1)
struct __attribute__ ((__packed__)) vmcb_seg {
u16 selector;
--
2.9.4
This CPUID provides the memory encryption support information on
AMD Platform. The complete description for CPUID leaf is available
in APM volume 2, Section 15.34
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/cpuid.c | 2 +-
arch/x86/kvm/svm.c | 6 ++++++
2 files changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 59ca2ee..372e969 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -599,7 +599,7 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
entry->edx = 0;
break;
case 0x80000000:
- entry->eax = min(entry->eax, 0x8000001a);
+ entry->eax = min(entry->eax, 0x8000001f);
break;
case 0x80000001:
entry->edx &= kvm_cpuid_8000_0001_edx_x86_features;
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 1cd7c78..256c9df 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -5131,6 +5131,12 @@ static void svm_set_supported_cpuid(u32 func, struct kvm_cpuid_entry2 *entry)
entry->edx |= SVM_FEATURE_NPT;
break;
+ case 0x8000001F:
+ /* Support memory encryption cpuid if host supports it */
+ if (boot_cpu_has(X86_FEATURE_SEV))
+ cpuid(0x8000001f, &entry->eax, &entry->ebx,
+ &entry->ecx, &entry->edx);
+
}
}
--
2.9.4
If hardware support memory encryption then KVM_MEMORY_REGISTER_RAM and
KVM_MEMORY_UNREGISTER_RAM ioctl's can be used by userspace to register/
unregister the guest memory regions which may contains the encrypted
data (e.g guest RAM, PCI BAR, SMRAM etc).
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/include/asm/kvm_host.h | 4 ++++
arch/x86/kvm/x86.c | 36 ++++++++++++++++++++++++++++++++++++
include/uapi/linux/kvm.h | 9 +++++++++
3 files changed, 49 insertions(+)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 99a0e11..4295f82 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1059,6 +1059,10 @@ struct kvm_x86_ops {
void (*setup_mce)(struct kvm_vcpu *vcpu);
int (*memory_encryption_op)(struct kvm *kvm, void __user *argp);
+ int (*memory_encryption_register_ram)(struct kvm *kvm,
+ struct kvm_memory_encrypt_ram *ram);
+ int (*memory_encryption_unregister_ram)(struct kvm *kvm,
+ struct kvm_memory_encrypt_ram *ram);
};
struct kvm_arch_async_pf {
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index c9d3ff5..8febdb5 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3982,6 +3982,24 @@ static int kvm_vm_ioctl_memory_encryption_op(struct kvm *kvm, void __user *argp)
return -ENOTTY;
}
+static int kvm_vm_ioctl_mem_encrypt_register_ram(struct kvm *kvm,
+ struct kvm_memory_encrypt_ram *ram)
+{
+ if (kvm_x86_ops->memory_encryption_register_ram)
+ return kvm_x86_ops->memory_encryption_register_ram(kvm, ram);
+
+ return -ENOTTY;
+}
+
+static int kvm_vm_ioctl_mem_encrypt_unregister_ram(struct kvm *kvm,
+ struct kvm_memory_encrypt_ram *ram)
+{
+ if (kvm_x86_ops->memory_encryption_unregister_ram)
+ return kvm_x86_ops->memory_encryption_unregister_ram(kvm, ram);
+
+ return -ENOTTY;
+}
+
long kvm_arch_vm_ioctl(struct file *filp,
unsigned int ioctl, unsigned long arg)
{
@@ -4246,6 +4264,24 @@ long kvm_arch_vm_ioctl(struct file *filp,
r = kvm_vm_ioctl_memory_encryption_op(kvm, argp);
break;
}
+ case KVM_MEMORY_ENCRYPT_REGISTER_RAM: {
+ struct kvm_memory_encrypt_ram ram;
+
+ r = -EFAULT;
+ if (copy_from_user(&ram, argp, sizeof(ram)))
+ goto out;
+ r = kvm_vm_ioctl_mem_encrypt_register_ram(kvm, &ram);
+ break;
+ }
+ case KVM_MEMORY_ENCRYPT_UNREGISTER_RAM: {
+ struct kvm_memory_encrypt_ram ram;
+
+ r = -EFAULT;
+ if (copy_from_user(&ram, argp, sizeof(ram)))
+ goto out;
+ r = kvm_vm_ioctl_mem_encrypt_unregister_ram(kvm, &ram);
+ break;
+ }
default:
r = -ENOTTY;
}
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index ab3b711..6074065 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1357,6 +1357,15 @@ struct kvm_s390_ucas_mapping {
#define KVM_S390_SET_CMMA_BITS _IOW(KVMIO, 0xb9, struct kvm_s390_cmma_log)
/* Memory Encryption Commands */
#define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xba, unsigned long)
+#define KVM_MEMORY_ENCRYPT_REGISTER_RAM _IOR(KVMIO, 0xbb, \
+ struct kvm_memory_encrypt_ram)
+#define KVM_MEMORY_ENCRYPT_UNREGISTER_RAM _IOR(KVMIO, 0xbc, \
+ struct kvm_memory_encrypt_ram)
+
+struct kvm_memory_encrypt_ram {
+ __u64 address;
+ __u64 size;
+};
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
--
2.9.4
Define Secure Encrypted Virtualization (SEV) key management command id
and structure. The command definition is available in SEV KM [1] spec
0.14 and Documentation/virtual/kvm/amd-memory-encryption.txt
[1] http://support.amd.com/TechDocs/55766_SEV-KM API_Specification.pdf
Signed-off-by: Brijesh Singh <[email protected]>
---
include/uapi/linux/kvm.h | 148 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 148 insertions(+)
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 6074065..8decc88 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1367,6 +1367,154 @@ struct kvm_memory_encrypt_ram {
__u64 size;
};
+/* Secure Encrypted Virtualization command */
+enum sev_cmd_id {
+ /* Guest initialization commands */
+ KVM_SEV_INIT = 0,
+ KVM_SEV_ES_INIT,
+ /* Guest launch commands */
+ KVM_SEV_LAUNCH_START,
+ KVM_SEV_LAUNCH_UPDATE_DATA,
+ KVM_SEV_LAUNCH_UPDATE_VMSA,
+ KVM_SEV_LAUNCH_SECRET,
+ KVM_SEV_LAUNCH_MEASURE,
+ KVM_SEV_LAUNCH_FINISH,
+ /* Guest migration commands (outgoing) */
+ KVM_SEV_SEND_START,
+ KVM_SEV_SEND_UPDATE_DATA,
+ KVM_SEV_SEND_UPDATE_VMSA,
+ KVM_SEV_SEND_FINISH,
+ /* Guest migration commands (incoming) */
+ KVM_SEV_RECEIVE_START,
+ KVM_SEV_RECEIVE_UPDATE_DATA,
+ KVM_SEV_RECEIVE_UPDATE_VMSA,
+ KVM_SEV_RECEIVE_FINISH,
+ /* Guest status and debug commands */
+ KVM_SEV_GUEST_STATUS,
+ KVM_SEV_DBG_DECRYPT,
+ KVM_SEV_DBG_ENCRYPT,
+ /* Guest certificates commands */
+ KVM_SEV_CERT_EXPORT,
+
+ KVM_SEV_NR_MAX,
+};
+
+struct kvm_sev_cmd {
+ __u32 id;
+ __u64 data;
+ __u32 error;
+ __u32 sev_fd;
+};
+
+struct kvm_sev_launch_start {
+ __u32 handle;
+ __u32 policy;
+ __u64 dh_cert_address;
+ __u32 dh_cert_length;
+ __u64 session_address;
+ __u32 session_length;
+};
+
+struct kvm_sev_launch_update_data {
+ __u64 address;
+ __u32 length;
+};
+
+struct kvm_sev_launch_update_vmsa {
+ __u64 address;
+ __u32 length;
+};
+
+struct kvm_sev_launch_secret {
+ __u64 hdr_address;
+ __u32 hdr_length;
+ __u64 guest_address;
+ __u32 guest_length;
+ __u64 trans_address;
+ __u32 trans_length;
+};
+
+struct kvm_sev_launch_measure {
+ __u64 address;
+ __u32 length;
+};
+
+struct kvm_sev_send_start {
+ __u32 policy;
+ __u64 pdh_cert_address;
+ __u32 pdh_cert_length;
+ __u64 plat_cert_address;
+ __u32 plat_cert_length;
+ __u64 amd_cert_address;
+ __u32 amd_cert_length;
+ __u64 session_address;
+ __u32 session_length;
+};
+
+struct kvm_sev_send_update_data {
+ __u64 hdr_address;
+ __u32 hdr_length;
+ __u64 guest_address;
+ __u32 guest_length;
+ __u64 trans_address;
+ __u32 trans_length;
+};
+
+struct kvm_sev_send_update_vmsa {
+ __u64 hdr_address;
+ __u32 hdr_length;
+ __u64 guest_address;
+ __u32 guest_length;
+ __u64 trans_address;
+ __u32 trans_length;
+};
+
+struct kvm_sev_receive_start {
+ __u32 handle;
+ __u32 policy;
+ __u64 pdh_cert_address;
+ __u32 pdh_cert_length;
+ __u64 session_address;
+ __u32 session_length;
+};
+
+struct kvm_sev_receive_update_data {
+ __u64 hdr_address;
+ __u32 hdr_length;
+ __u64 guest_address;
+ __u32 guest_length;
+ __u64 trans_address;
+ __u32 trans_length;
+};
+
+struct kvm_sev_receive_update_vmsa {
+ __u64 hdr_address;
+ __u32 hdr_length;
+ __u64 guest_address;
+ __u32 guest_length;
+ __u64 trans_address;
+ __u32 trans_length;
+};
+
+struct kvm_sev_guest_status {
+ __u32 handle;
+ __u32 policy;
+ __u32 state;
+};
+
+struct kvm_sev_dbg {
+ __u64 src_addr;
+ __u64 dst_addr;
+ __u32 length;
+};
+
+struct kvm_sev_cert_export {
+ __u64 pdh_cert_address;
+ __u32 pdh_cert_length;
+ __u64 cert_chain_address;
+ __u32 cert_chain_length;
+};
+
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
#define KVM_DEV_ASSIGN_MASK_INTX (1 << 2)
--
2.9.4
The patch adds a new member (sev_info) in 'struct kvm_arch', and
setter/getter functions for the sev_info field.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/include/asm/kvm_host.h | 9 +++++++++
arch/x86/kvm/svm.c | 45 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 54 insertions(+)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 4295f82..150177e 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -742,6 +742,13 @@ enum kvm_irqchip_mode {
KVM_IRQCHIP_SPLIT, /* created with KVM_CAP_SPLIT_IRQCHIP */
};
+struct kvm_sev_info {
+ bool active; /* SEV enabled guest */
+ unsigned int handle; /* firmware handle */
+ unsigned int asid; /* asid for this guest */
+ int sev_fd; /* SEV device fd */
+};
+
struct kvm_arch {
unsigned int n_used_mmu_pages;
unsigned int n_requested_mmu_pages;
@@ -829,6 +836,8 @@ struct kvm_arch {
bool x2apic_format;
bool x2apic_broadcast_quirk_disabled;
+
+ struct kvm_sev_info sev_info;
};
struct kvm_vm_stat {
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 256c9df..2a5a03a 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -322,6 +322,51 @@ enum {
/* Secure Encrypted Virtualization */
static unsigned int max_sev_asid;
+static inline struct kvm_sev_info *to_sev_info(struct kvm *kvm)
+{
+ return &kvm->arch.sev_info;
+}
+
+static inline void sev_set_active(struct kvm *kvm)
+{
+ to_sev_info(kvm)->active = true;
+}
+
+static inline unsigned int sev_get_handle(struct kvm *kvm)
+{
+ return to_sev_info(kvm)->handle;
+}
+
+static inline bool sev_guest(struct kvm *kvm)
+{
+ return to_sev_info(kvm)->active;
+}
+
+static inline int sev_get_asid(struct kvm *kvm)
+{
+ return to_sev_info(kvm)->asid;
+}
+
+static inline int sev_get_fd(struct kvm *kvm)
+{
+ return to_sev_info(kvm)->sev_fd;
+}
+
+static inline void sev_set_asid(struct kvm *kvm, int asid)
+{
+ to_sev_info(kvm)->asid = asid;
+}
+
+static inline void sev_set_handle(struct kvm *kvm, unsigned int handle)
+{
+ to_sev_info(kvm)->handle = handle;
+}
+
+static inline void sev_set_fd(struct kvm *kvm, int fd)
+{
+ to_sev_info(kvm)->sev_fd = fd;
+}
+
static inline void mark_all_dirty(struct vmcb *vmcb)
{
vmcb->control.clean = 0;
--
2.9.4
SEV hardware uses ASIDs to associate memory encryption key with the
guest VMs. During the guest creation time, we use SEV_CMD_ACTIVATE
command to bind a particular ASID to the guest. Lets make sure that
VMCB is programmed with the binded ASID before a VMRUN.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 49 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index e99a572..72f7c27 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -213,6 +213,9 @@ struct vcpu_svm {
*/
struct list_head ir_list;
spinlock_t ir_list_lock;
+
+ /* which host cpu was used for running this vcpu */
+ unsigned int last_cpuid;
};
/*
@@ -573,6 +576,8 @@ struct svm_cpu_data {
struct kvm_ldttss_desc *tss_desc;
struct page *save_area;
+
+ struct vmcb **sev_vmcbs; /* index = sev_asid, value = vmcb pointer */
};
static DEFINE_PER_CPU(struct svm_cpu_data *, svm_data);
@@ -886,6 +891,7 @@ static void svm_cpu_uninit(int cpu)
return;
per_cpu(svm_data, raw_smp_processor_id()) = NULL;
+ kfree(sd->sev_vmcbs);
__free_page(sd->save_area);
kfree(sd);
}
@@ -904,6 +910,14 @@ static int svm_cpu_init(int cpu)
if (!sd->save_area)
goto err_1;
+ if (svm_sev_enabled()) {
+ sd->sev_vmcbs = kmalloc((max_sev_asid + 1) * sizeof(void *),
+ GFP_KERNEL);
+ r = -ENOMEM;
+ if (!sd->sev_vmcbs)
+ goto err_1;
+ }
+
per_cpu(svm_data, cpu) = sd;
return 0;
@@ -4442,12 +4456,40 @@ static void reload_tss(struct kvm_vcpu *vcpu)
load_TR_desc();
}
+static void pre_sev_run(struct vcpu_svm *svm)
+{
+ int cpu = raw_smp_processor_id();
+ int asid = sev_get_asid(svm->vcpu.kvm);
+ struct svm_cpu_data *sd = per_cpu(svm_data, cpu);
+
+ /* Assign the asid allocated with this SEV guest */
+ svm->vmcb->control.asid = asid;
+
+ /*
+ * Flush guest TLB:
+ *
+ * 1) when different VMCB for the same ASID is to be run on the same host CPU.
+ * 2) or this VMCB was executed on different host cpu in previous VMRUNs.
+ */
+ if (sd->sev_vmcbs[asid] == svm->vmcb &&
+ svm->last_cpuid == cpu)
+ return;
+
+ svm->last_cpuid = cpu;
+ sd->sev_vmcbs[asid] = svm->vmcb;
+ svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ASID;
+ mark_dirty(svm->vmcb, VMCB_ASID);
+}
+
static void pre_svm_run(struct vcpu_svm *svm)
{
int cpu = raw_smp_processor_id();
struct svm_cpu_data *sd = per_cpu(svm_data, cpu);
+ if (sev_guest(svm->vcpu.kvm))
+ return pre_sev_run(svm);
+
/* FIXME: handle wraparound of asid_generation */
if (svm->asid_generation != sd->asid_generation)
new_asid(svm, sd);
@@ -5523,10 +5565,16 @@ static int sev_asid_new(void)
static void sev_asid_free(int asid)
{
- int pos;
+ struct svm_cpu_data *sd;
+ int pos, cpu;
pos = asid - 1;
clear_bit(pos, sev_asid_bitmap);
+
+ for_each_possible_cpu(cpu) {
+ sd = per_cpu(svm_data, cpu);
+ sd->sev_vmcbs[pos] = NULL;
+ }
}
static int sev_firmware_init(int *error)
--
2.9.4
The command initializes the SEV firmware and allocate a new ASID for
this guest from SEV ASID pool. The firmware must be initialized before
we issue guest launch command to create a new encryption context.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 187 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 2a5a03a..e99a572 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -37,6 +37,8 @@
#include <linux/amd-iommu.h>
#include <linux/hashtable.h>
#include <linux/frame.h>
+#include <linux/psp-sev.h>
+#include <linux/file.h>
#include <asm/apic.h>
#include <asm/perf_event.h>
@@ -321,6 +323,14 @@ enum {
/* Secure Encrypted Virtualization */
static unsigned int max_sev_asid;
+static unsigned long *sev_asid_bitmap;
+static int sev_asid_new(void);
+static void sev_asid_free(int asid);
+
+static bool svm_sev_enabled(void)
+{
+ return !!max_sev_asid;
+}
static inline struct kvm_sev_info *to_sev_info(struct kvm *kvm)
{
@@ -1093,6 +1103,12 @@ static __init void sev_hardware_setup(void)
if (!nguests)
return;
+ /* Initialize SEV ASID bitmap */
+ sev_asid_bitmap = kcalloc(BITS_TO_LONGS(nguests),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (IS_ERR(sev_asid_bitmap))
+ return;
+
max_sev_asid = nguests;
}
@@ -1184,10 +1200,18 @@ static __init int svm_hardware_setup(void)
return r;
}
+static __exit void sev_hardware_unsetup(void)
+{
+ kfree(sev_asid_bitmap);
+}
+
static __exit void svm_hardware_unsetup(void)
{
int cpu;
+ if (svm_sev_enabled())
+ sev_hardware_unsetup();
+
for_each_possible_cpu(cpu)
svm_cpu_uninit(cpu);
@@ -1373,6 +1397,9 @@ static void init_vmcb(struct vcpu_svm *svm)
svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK;
}
+ if (sev_guest(svm->vcpu.kvm))
+ svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE;
+
mark_all_dirty(svm->vmcb);
enable_gif(svm);
@@ -1483,6 +1510,51 @@ static inline int avic_free_vm_id(int id)
return 0;
}
+static int sev_platform_get_state(int *state, int *error)
+{
+ int ret;
+ struct sev_data_status *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = sev_platform_status(data, error);
+ if (!ret)
+ *state = data->state;
+
+ kfree(data);
+ return ret;
+}
+
+static void sev_firmware_uninit(void)
+{
+ int rc, state, error;
+
+ rc = sev_platform_get_state(&state, &error);
+ if (rc) {
+ pr_err("SEV: failed to get firmware state (%#x)\n",
+ error);
+ return;
+ }
+
+ /* If we are in initialized state then uninitialize it */
+ if (state == SEV_STATE_INIT)
+ sev_platform_shutdown(&error);
+
+}
+
+static void sev_vm_destroy(struct kvm *kvm)
+{
+ int state, error;
+
+ if (!sev_guest(kvm))
+ return;
+
+ sev_asid_free(sev_get_asid(kvm));
+ sev_firmware_uninit();
+}
+
static void avic_vm_destroy(struct kvm *kvm)
{
unsigned long flags;
@@ -1503,6 +1575,12 @@ static void avic_vm_destroy(struct kvm *kvm)
spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags);
}
+static void svm_vm_destroy(struct kvm *kvm)
+{
+ avic_vm_destroy(kvm);
+ sev_vm_destroy(kvm);
+}
+
static int avic_vm_init(struct kvm *kvm)
{
unsigned long flags;
@@ -5428,6 +5506,112 @@ static void svm_setup_mce(struct kvm_vcpu *vcpu)
vcpu->arch.mcg_cap &= 0x1ff;
}
+static int sev_asid_new(void)
+{
+ int pos;
+
+ if (!max_sev_asid)
+ return -EINVAL;
+
+ pos = find_first_zero_bit(sev_asid_bitmap, max_sev_asid);
+ if (pos >= max_sev_asid)
+ return -EBUSY;
+
+ set_bit(pos, sev_asid_bitmap);
+ return pos + 1;
+}
+
+static void sev_asid_free(int asid)
+{
+ int pos;
+
+ pos = asid - 1;
+ clear_bit(pos, sev_asid_bitmap);
+}
+
+static int sev_firmware_init(int *error)
+{
+ int ret, state;
+
+ ret = sev_platform_get_state(&state, error);
+ if (ret)
+ return ret;
+
+ /*
+ * If SEV firmware is in uninitialized state, lets initialize the
+ * firmware before issuing guest commands.
+ */
+ if (state == SEV_STATE_UNINIT) {
+ struct sev_data_init *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = sev_platform_init(data, error);
+ kfree(data);
+ }
+
+ return ret;
+}
+
+static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ int asid, ret;
+ struct fd f;
+
+ f = fdget(argp->sev_fd);
+ if (!f.file)
+ return -EBADF;
+
+ /* initialize the SEV firmware */
+ ret = sev_firmware_init(&argp->error);
+ if (ret)
+ goto e_err;
+
+ /* allocate asid from SEV pool */
+ ret = -ENOTTY;
+ asid = sev_asid_new();
+ if (asid < 0) {
+ pr_err("SEV: failed to get free asid\n");
+ sev_platform_shutdown(&argp->error);
+ goto e_err;
+ }
+
+ sev_set_active(kvm);
+ sev_set_asid(kvm, asid);
+ sev_set_fd(kvm, argp->sev_fd);
+ ret = 0;
+e_err:
+ fdput(f);
+ return ret;
+}
+
+static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
+{
+ struct kvm_sev_cmd sev_cmd;
+ int r = -ENOTTY;
+
+ if (copy_from_user(&sev_cmd, argp, sizeof(struct kvm_sev_cmd)))
+ return -EFAULT;
+
+ mutex_lock(&kvm->lock);
+
+ switch (sev_cmd.id) {
+ case KVM_SEV_INIT: {
+ r = sev_guest_init(kvm, &sev_cmd);
+ break;
+ }
+ default:
+ break;
+ }
+
+ mutex_unlock(&kvm->lock);
+ if (copy_to_user(argp, &sev_cmd, sizeof(struct kvm_sev_cmd)))
+ r = -EFAULT;
+ return r;
+}
+
static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.cpu_has_kvm_support = has_svm,
.disabled_by_bios = is_disabled,
@@ -5444,7 +5628,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.vcpu_reset = svm_vcpu_reset,
.vm_init = avic_vm_init,
- .vm_destroy = avic_vm_destroy,
+ .vm_destroy = svm_vm_destroy,
.prepare_guest_switch = svm_prepare_guest_switch,
.vcpu_load = svm_vcpu_load,
@@ -5540,6 +5724,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.deliver_posted_interrupt = svm_deliver_avic_intr,
.update_pi_irte = svm_update_pi_irte,
.setup_mce = svm_setup_mce,
+
+ .memory_encryption_op = svm_memory_encryption_op,
};
static int __init svm_init(void)
--
2.9.4
The command is used for encrypting the guest memory region using the VM
encryption key (VEK) created during LAUNCH_START.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 3e325578..91b070f 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -39,6 +39,8 @@
#include <linux/frame.h>
#include <linux/psp-sev.h>
#include <linux/file.h>
+#include <linux/pagemap.h>
+#include <linux/swap.h>
#include <asm/apic.h>
#include <asm/perf_event.h>
@@ -331,6 +333,7 @@ static int sev_asid_new(void);
static void sev_asid_free(int asid);
static void sev_deactivate_handle(struct kvm *kvm, int *error);
static void sev_decommission_handle(struct kvm *kvm, int *error);
+#define __sme_page_pa(x) __sme_set(page_to_pfn(x) << PAGE_SHIFT)
static bool svm_sev_enabled(void)
{
@@ -5796,6 +5799,164 @@ static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static struct page **sev_pin_memory(unsigned long uaddr, unsigned long ulen,
+ unsigned long *n, int write)
+{
+ unsigned long npages, pinned, size;
+ struct page **pages;
+ int first, last;
+
+ /* Get number of pages */
+ first = (uaddr & PAGE_MASK) >> PAGE_SHIFT;
+ last = ((uaddr + ulen - 1) & PAGE_MASK) >> PAGE_SHIFT;
+ npages = (last - first + 1);
+
+ /* Avoid using vmalloc for smaller buffer */
+ size = npages * sizeof(struct page *);
+ if (size > PAGE_SIZE)
+ pages = vmalloc(size);
+ else
+ pages = kmalloc(size, GFP_KERNEL);
+
+ if (!pages)
+ return NULL;
+
+ /* pin the user virtual address */
+ pinned = get_user_pages_fast(uaddr, npages, write ? FOLL_WRITE : 0,
+ pages);
+ if (pinned != npages) {
+ pr_err("failed to pin %ld pages (got %ld)\n", npages, pinned);
+ goto err;
+ }
+
+ *n = npages;
+ return pages;
+err:
+ if (pinned > 0)
+ release_pages(pages, pinned, 0);
+ kvfree(pages);
+
+ return NULL;
+}
+
+static void sev_unpin_memory(struct page **pages, unsigned long npages)
+{
+ release_pages(pages, npages, 0);
+ kvfree(pages);
+}
+
+static void sev_clflush_pages(struct page *pages[], unsigned long npages)
+{
+ uint8_t *page_virtual;
+ unsigned long i;
+
+ if (npages == 0 || pages == NULL)
+ return;
+
+ for (i = 0; i < npages; i++) {
+ page_virtual = kmap_atomic(pages[i]);
+ clflush_cache_range(page_virtual, PAGE_SIZE);
+ kunmap_atomic(page_virtual);
+ }
+}
+
+static int get_num_contig_pages(int idx, struct page **inpages,
+ unsigned long npages)
+{
+ int i = idx + 1, pages = 1;
+ unsigned long paddr, next_paddr;
+
+ /* find the number of contiguous pages starting from idx */
+ paddr = __sme_page_pa(inpages[idx]);
+ while (i < npages) {
+ next_paddr = __sme_page_pa(inpages[i++]);
+ if ((paddr + PAGE_SIZE) == next_paddr) {
+ pages++;
+ paddr = next_paddr;
+ continue;
+ }
+ break;
+ }
+
+ return pages;
+}
+
+static int sev_launch_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ unsigned long vaddr, vaddr_end, next_vaddr, npages, size;
+ struct kvm_sev_launch_update_data params;
+ struct sev_data_launch_update_data *data;
+ struct page **inpages;
+ int i, ret, pages;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ if (copy_from_user(¶ms, (void *)argp->data,
+ sizeof(struct kvm_sev_launch_update_data)))
+ return -EFAULT;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ vaddr = params.address;
+ size = params.length;
+ vaddr_end = vaddr + size;
+
+ /* lock the user memory */
+ inpages = sev_pin_memory(vaddr, size, &npages, 1);
+ if (!inpages) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+
+ /*
+ * invalidate the cache to ensure that DRAM has recent content before
+ * calling the SEV commands.
+ */
+ sev_clflush_pages(inpages, npages);
+
+ for (i = 0; vaddr < vaddr_end; vaddr = next_vaddr, i += pages) {
+ int offset, len;
+
+ /*
+ * since user buffer may not be page aligned, calculate the
+ * offset within the page.
+ */
+ offset = vaddr & (PAGE_SIZE - 1);
+
+ /*
+ * calculate the number of pages that can be encrypted in one go
+ */
+ pages = get_num_contig_pages(i, inpages, npages);
+
+ len = min_t(size_t, ((pages * PAGE_SIZE) - offset), size);
+
+ data->handle = sev_get_handle(kvm);
+ data->length = len;
+ data->address = __sme_page_pa(inpages[i]) + offset;
+ ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_DATA, data,
+ &argp->error);
+ if (ret)
+ goto e_unpin;
+
+ size -= len;
+ next_vaddr = vaddr + len;
+ }
+e_unpin:
+ /* content of memory is updated, mark pages dirty */
+ for (i = 0; i < npages; i++) {
+ set_page_dirty_lock(inpages[i]);
+ mark_page_accessed(inpages[i]);
+ }
+ /* unlock the user pages */
+ sev_unpin_memory(inpages, npages);
+e_free:
+ kfree(data);
+ return ret;
+}
+
static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
{
struct kvm_sev_cmd sev_cmd;
@@ -5815,6 +5976,10 @@ static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
r = sev_launch_start(kvm, &sev_cmd);
break;
}
+ case KVM_SEV_LAUNCH_UPDATE_DATA: {
+ r = sev_launch_update_data(kvm, &sev_cmd);
+ break;
+ }
default:
break;
}
--
2.9.4
The command is used to bootstrap SEV guest from unencrypted boot images.
The command creates a new VM encryption key (VEK) using the guest owner's
policy, public DH certificates, and session information.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 165 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 72f7c27..3e325578 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -329,6 +329,8 @@ static unsigned int max_sev_asid;
static unsigned long *sev_asid_bitmap;
static int sev_asid_new(void);
static void sev_asid_free(int asid);
+static void sev_deactivate_handle(struct kvm *kvm, int *error);
+static void sev_decommission_handle(struct kvm *kvm, int *error);
static bool svm_sev_enabled(void)
{
@@ -1565,6 +1567,12 @@ static void sev_vm_destroy(struct kvm *kvm)
if (!sev_guest(kvm))
return;
+ /* release the firmware resources for this guest */
+ if (sev_get_handle(kvm)) {
+ sev_deactivate_handle(kvm, &error);
+ sev_decommission_handle(kvm, &error);
+ }
+
sev_asid_free(sev_get_asid(kvm));
sev_firmware_uninit();
}
@@ -5635,6 +5643,159 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int sev_issue_cmd(struct kvm *kvm, int id, void *data, int *error)
+{
+ int fd = sev_get_fd(kvm);
+ struct fd f;
+ int ret;
+
+ f = fdget(fd);
+ if (!f.file)
+ return -EBADF;
+
+ ret = sev_issue_cmd_external_user(f.file, id, data, error);
+ fdput(f);
+
+ return ret;
+}
+
+static void sev_decommission_handle(struct kvm *kvm, int *error)
+{
+ struct sev_data_decommission *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->handle = sev_get_handle(kvm);
+ sev_guest_decommission(data, error);
+ kfree(data);
+}
+
+static void sev_deactivate_handle(struct kvm *kvm, int *error)
+{
+ struct sev_data_deactivate *data;
+ int ret;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->handle = sev_get_handle(kvm);
+ ret = sev_guest_deactivate(data, error);
+ if (ret)
+ goto e_free;
+
+ wbinvd_on_all_cpus();
+
+ sev_guest_df_flush(error);
+e_free:
+ kfree(data);
+}
+
+static int sev_activate_asid(struct kvm *kvm, unsigned int handle, int *error)
+{
+ struct sev_data_activate *data;
+ int asid = sev_get_asid(kvm);
+ int ret;
+
+ wbinvd_on_all_cpus();
+
+ ret = sev_guest_df_flush(error);
+ if (ret)
+ return ret;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->handle = handle;
+ data->asid = asid;
+ ret = sev_guest_activate(data, error);
+ if (ret)
+ goto e_err;
+
+ sev_set_handle(kvm, handle);
+e_err:
+ kfree(data);
+ return ret;
+}
+
+static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ struct sev_data_launch_start *start = NULL;
+ struct kvm_sev_launch_start params;
+ void *dh_cert_addr = NULL;
+ void *session_addr = NULL;
+ int *error = &argp->error;
+ int ret;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ ret = -EFAULT;
+ if (copy_from_user(¶ms, (void *)argp->data,
+ sizeof(struct kvm_sev_launch_start)))
+ goto e_free;
+
+ ret = -ENOMEM;
+ start = kzalloc(sizeof(*start), GFP_KERNEL);
+ if (!start)
+ goto e_free;
+
+ /* Bit 15:6 reserved, must be 0 */
+ start->policy = params.policy & ~0xffc0;
+
+ if (params.dh_cert_length && params.dh_cert_address) {
+ ret = -ENOMEM;
+ dh_cert_addr = kmalloc(params.dh_cert_length, GFP_KERNEL);
+ if (!dh_cert_addr)
+ goto e_free;
+
+ ret = -EFAULT;
+ if (copy_from_user(dh_cert_addr, (void *)params.dh_cert_address,
+ params.dh_cert_length))
+ goto e_free;
+
+ start->dh_cert_address = __sme_set(__pa(dh_cert_addr));
+ start->dh_cert_length = params.dh_cert_length;
+ }
+
+ if (params.session_length && params.session_address) {
+ ret = -ENOMEM;
+ session_addr = kmalloc(params.session_length, GFP_KERNEL);
+ if (!session_addr)
+ goto e_free;
+
+ ret = -EFAULT;
+ if (copy_from_user(session_addr, (void *)params.session_address,
+ params.session_length))
+ goto e_free;
+
+ start->session_address = __sme_set(__pa(session_addr));
+ start->session_length = params.session_length;
+ }
+
+ start->handle = params.handle;
+ ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_START, start, error);
+ if (ret)
+ goto e_free;
+
+ ret = sev_activate_asid(kvm, start->handle, error);
+ if (ret)
+ goto e_free;
+
+ params.handle = start->handle;
+ if (copy_to_user((void *) argp->data, ¶ms,
+ sizeof(struct kvm_sev_launch_start)))
+ ret = -EFAULT;
+e_free:
+ kfree(dh_cert_addr);
+ kfree(session_addr);
+ kfree(start);
+ return ret;
+}
+
static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
{
struct kvm_sev_cmd sev_cmd;
@@ -5650,6 +5811,10 @@ static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
r = sev_guest_init(kvm, &sev_cmd);
break;
}
+ case KVM_SEV_LAUNCH_START: {
+ r = sev_launch_start(kvm, &sev_cmd);
+ break;
+ }
default:
break;
}
--
2.9.4
The command is used for querying the SEV guest status.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 7a77197..21f85e1 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -6024,6 +6024,40 @@ static int sev_launch_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int sev_guest_status(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ struct kvm_sev_guest_status params;
+ struct sev_data_guest_status *data;
+ int ret;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ if (copy_from_user(¶ms, (void *) argp->data,
+ sizeof(struct kvm_sev_guest_status)))
+ return -EFAULT;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->handle = sev_get_handle(kvm);
+ ret = sev_issue_cmd(kvm, SEV_CMD_GUEST_STATUS, data, &argp->error);
+ if (ret)
+ goto e_free;
+
+ params.policy = data->policy;
+ params.state = data->state;
+ params.handle = data->handle;
+
+ if (copy_to_user((void *) argp->data, ¶ms,
+ sizeof(struct kvm_sev_guest_status)))
+ ret = -EFAULT;
+e_free:
+ kfree(data);
+ return ret;
+}
+
static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
{
struct kvm_sev_cmd sev_cmd;
@@ -6055,6 +6089,10 @@ static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
r = sev_launch_finish(kvm, &sev_cmd);
break;
}
+ case KVM_SEV_GUEST_STATUS: {
+ r = sev_guest_status(kvm, &sev_cmd);
+ break;
+ }
default:
break;
}
--
2.9.4
The command is used for finializing the SEV guest launch process.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 9b672eb..7a77197 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -6005,6 +6005,25 @@ static int sev_launch_measure(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int sev_launch_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ struct sev_data_launch_finish *data;
+ int ret;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->handle = sev_get_handle(kvm);
+ ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_FINISH, data, &argp->error);
+
+ kfree(data);
+ return ret;
+}
+
static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
{
struct kvm_sev_cmd sev_cmd;
@@ -6032,6 +6051,10 @@ static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
r = sev_launch_measure(kvm, &sev_cmd);
break;
}
+ case KVM_SEV_LAUNCH_FINISH: {
+ r = sev_launch_finish(kvm, &sev_cmd);
+ break;
+ }
default:
break;
}
--
2.9.4
The command is used to retrieve the measurement of memory encrypted
through the LAUNCH_UPDATE_DATA command. This measurement can be used
for attestation purposes.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 52 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 91b070f..9b672eb 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -5957,6 +5957,54 @@ static int sev_launch_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int sev_launch_measure(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ struct sev_data_launch_measure *data = NULL;
+ struct kvm_sev_launch_measure params;
+ void *addr = NULL;
+ int ret;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ if (copy_from_user(¶ms, (void *)argp->data,
+ sizeof(struct kvm_sev_launch_measure)))
+ return -EFAULT;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (params.address && params.length) {
+ ret = -EFAULT;
+ addr = kzalloc(params.length, GFP_KERNEL);
+ if (!addr)
+ goto e_free;
+ data->address = __psp_pa(addr);
+ data->length = params.length;
+ }
+
+ data->handle = sev_get_handle(kvm);
+ ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_MEASURE, data, &argp->error);
+
+ /* copy the measurement to userspace */
+ if (addr &&
+ copy_to_user((void *)params.address, addr, params.length)) {
+ ret = -EFAULT;
+ goto e_free;
+ }
+
+ params.length = data->length;
+ if (copy_to_user((void *)argp->data, ¶ms,
+ sizeof(struct kvm_sev_launch_measure)))
+ ret = -EFAULT;
+
+e_free:
+ kfree(addr);
+ kfree(data);
+ return ret;
+}
+
static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
{
struct kvm_sev_cmd sev_cmd;
@@ -5980,6 +6028,10 @@ static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
r = sev_launch_update_data(kvm, &sev_cmd);
break;
}
+ case KVM_SEV_LAUNCH_MEASURE: {
+ r = sev_launch_measure(kvm, &sev_cmd);
+ break;
+ }
default:
break;
}
--
2.9.4
When SEV is active, on #NPF the page fault address will contain C-bit.
We must clear the C-bit before handling the fault.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 0bbd050..64b9f60 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -2321,7 +2321,7 @@ static void svm_set_dr7(struct kvm_vcpu *vcpu, unsigned long value)
static int pf_interception(struct vcpu_svm *svm)
{
- u64 fault_address = svm->vmcb->control.exit_info_2;
+ u64 fault_address = __sme_clr(svm->vmcb->control.exit_info_2);
u64 error_code = svm->vmcb->control.exit_info_1;
return kvm_handle_page_fault(&svm->vcpu, error_code, fault_address,
--
2.9.4
Extend kvm_x86_ops to add memory_encyption_enabled() ops. It returns a
boolean indicating whether memory encryption is enabled on the VCPU.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/svm.c | 8 ++++++++
2 files changed, 9 insertions(+)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index a91aadf..a14d4dd 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1073,6 +1073,7 @@ struct kvm_x86_ops {
struct kvm_memory_encrypt_ram *ram);
int (*memory_encryption_unregister_ram)(struct kvm *kvm,
struct kvm_memory_encrypt_ram *ram);
+ bool (*memory_encryption_enabled)(struct kvm_vcpu *vcpu);
};
struct kvm_arch_async_pf {
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index cdb1cf3..0bbd050 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -6548,6 +6548,12 @@ static int mem_encrypt_unregister_ram(struct kvm *kvm,
return 0;
}
+static bool mem_encrypt_enabled(struct kvm_vcpu *vcpu)
+{
+ return !!(to_svm(vcpu)->vmcb->control.nested_ctl &
+ SVM_NESTED_CTL_SEV_ENABLE);
+}
+
static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.cpu_has_kvm_support = has_svm,
.disabled_by_bios = is_disabled,
@@ -6664,6 +6670,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.memory_encryption_op = svm_memory_encryption_op,
.memory_encryption_register_ram = mem_encrypt_register_ram,
.memory_encryption_unregister_ram = mem_encrypt_unregister_ram,
+ .memory_encryption_enabled = mem_encrypt_enabled,
+
};
static int __init svm_init(void)
--
2.9.4
The SEV memory encryption engine uses a tweak such that two identical
plaintexts at different location will have a different ciphertexts.
So swapping or moving ciphertexts of two pages will not result in
plaintexts being swapped. Relocating (or migrating) a physical backing
pages for SEV guest will require some additional steps. The current SEV
key management spec does not provide commands to swap or migrate (move)
ciphertexts. For now, we pin the guest memory registered through
KVM_MEMORY_ENCRYPT_REGISTER_RAM ioctl.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/include/asm/kvm_host.h | 1 +
arch/x86/kvm/svm.c | 113 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 114 insertions(+)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 150177e..a91aadf 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -747,6 +747,7 @@ struct kvm_sev_info {
unsigned int handle; /* firmware handle */
unsigned int asid; /* asid for this guest */
int sev_fd; /* SEV device fd */
+ struct list_head ram_list; /* list of registered ram */
};
struct kvm_arch {
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 75dcaa9..cdb1cf3 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -333,8 +333,19 @@ static int sev_asid_new(void);
static void sev_asid_free(int asid);
static void sev_deactivate_handle(struct kvm *kvm, int *error);
static void sev_decommission_handle(struct kvm *kvm, int *error);
+static void sev_unpin_memory(struct page **pages, unsigned long npages);
+
#define __sme_page_pa(x) __sme_set(page_to_pfn(x) << PAGE_SHIFT)
+struct kvm_sev_pin_ram {
+ struct list_head list;
+ unsigned long npages;
+ struct page **pages;
+ struct kvm_memory_encrypt_ram userspace;
+};
+
+static void __mem_encrypt_unregister_ram(struct kvm_sev_pin_ram *ram);
+
static bool svm_sev_enabled(void)
{
return !!max_sev_asid;
@@ -385,6 +396,11 @@ static inline void sev_set_fd(struct kvm *kvm, int fd)
to_sev_info(kvm)->sev_fd = fd;
}
+static inline struct list_head *sev_get_ram_list(struct kvm *kvm)
+{
+ return &to_sev_info(kvm)->ram_list;
+}
+
static inline void mark_all_dirty(struct vmcb *vmcb)
{
vmcb->control.clean = 0;
@@ -1566,10 +1582,24 @@ static void sev_firmware_uninit(void)
static void sev_vm_destroy(struct kvm *kvm)
{
int state, error;
+ struct list_head *pos, *q;
+ struct kvm_sev_pin_ram *ram;
+ struct list_head *head = sev_get_ram_list(kvm);
if (!sev_guest(kvm))
return;
+ /*
+ * if userspace was terminated before unregistering the memory region
+ * then lets unpin all the registered memory.
+ */
+ if (!list_empty(head)) {
+ list_for_each_safe(pos, q, head) {
+ ram = list_entry(pos, struct kvm_sev_pin_ram, list);
+ __mem_encrypt_unregister_ram(ram);
+ }
+ }
+
/* release the firmware resources for this guest */
if (sev_get_handle(kvm)) {
sev_deactivate_handle(kvm, &error);
@@ -5640,6 +5670,7 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
sev_set_active(kvm);
sev_set_asid(kvm, asid);
sev_set_fd(kvm, argp->sev_fd);
+ INIT_LIST_HEAD(sev_get_ram_list(kvm));
ret = 0;
e_err:
fdput(f);
@@ -6437,6 +6468,86 @@ static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
return r;
}
+static int mem_encrypt_register_ram(struct kvm *kvm,
+ struct kvm_memory_encrypt_ram *ram)
+{
+ struct list_head *head = sev_get_ram_list(kvm);
+ struct kvm_sev_pin_ram *pin_ram;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ pin_ram = kzalloc(sizeof(*pin_ram), GFP_KERNEL);
+ if (!pin_ram)
+ return -ENOMEM;
+
+ pin_ram->pages = sev_pin_memory(ram->address, ram->size,
+ &pin_ram->npages, 1);
+ if (!pin_ram->pages)
+ goto e_free;
+
+ /*
+ * Guest may change the memory encryption attribute from C=0 -> C=1
+ * for this memory range. Lets make sure caches are flushed to ensure
+ * that guest data gets written into memory with correct C-bit.
+ */
+ sev_clflush_pages(pin_ram->pages, pin_ram->npages);
+
+ pin_ram->userspace.address = ram->address;
+ pin_ram->userspace.size = ram->size;
+ list_add_tail(&pin_ram->list, head);
+ return 0;
+e_free:
+ kfree(pin_ram);
+ return 1;
+}
+
+static struct kvm_sev_pin_ram *sev_find_pinned_ram(struct kvm *kvm,
+ struct kvm_memory_encrypt_ram *ram)
+{
+ struct list_head *head = sev_get_ram_list(kvm);
+ struct kvm_sev_pin_ram *i;
+
+ list_for_each_entry(i, head, list) {
+ if (i->userspace.address == ram->address &&
+ i->userspace.size == ram->size)
+ return i;
+ }
+
+ return NULL;
+}
+
+static void __mem_encrypt_unregister_ram(struct kvm_sev_pin_ram *ram)
+{
+ /*
+ * Guest may have changed the memory encryption attribute from
+ * C=0 -> C=1. Lets make sure caches are flushed to ensure in data
+ * gets written into memory with correct C-bit.
+ */
+ sev_clflush_pages(ram->pages, ram->npages);
+
+ sev_unpin_memory(ram->pages, ram->npages);
+ list_del(&ram->list);
+ kfree(ram);
+}
+
+static int mem_encrypt_unregister_ram(struct kvm *kvm,
+ struct kvm_memory_encrypt_ram *ram)
+{
+ struct kvm_sev_pin_ram *pinned_ram;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ pinned_ram = sev_find_pinned_ram(kvm, ram);
+ if (!pinned_ram)
+ return -EINVAL;
+
+ __mem_encrypt_unregister_ram(pinned_ram);
+
+ return 0;
+}
+
static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.cpu_has_kvm_support = has_svm,
.disabled_by_bios = is_disabled,
@@ -6551,6 +6662,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.setup_mce = svm_setup_mce,
.memory_encryption_op = svm_memory_encryption_op,
+ .memory_encryption_register_ram = mem_encrypt_register_ram,
+ .memory_encryption_unregister_ram = mem_encrypt_unregister_ram,
};
static int __init svm_init(void)
--
2.9.4
The command copies a plain text into guest memory and encrypts it using
the VM encryption key. The command will be used for debug purposes
(e.g setting breakpoint through gdbserver)
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 174 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 933384a..75dcaa9 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -6214,6 +6214,176 @@ static int sev_dbg_decrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int __sev_dbg_enc(struct kvm *kvm, unsigned long __user vaddr,
+ unsigned long paddr, unsigned long __user dst_vaddr,
+ unsigned long dst_paddr, int size, int *error)
+{
+ struct page *src_tpage = NULL;
+ struct page *dst_tpage = NULL;
+ int ret, len = size;
+
+ /*
+ * Debug encrypt command works with 16-byte aligned inputs. Function
+ * handles the alingment issue as below:
+ *
+ * case 1
+ * If source buffer is not 16-byte aligned then we copy the data from
+ * source buffer into a PAGE aligned intermediate (src_tpage) buffer
+ * and use this intermediate buffer as source buffer
+ *
+ * case 2
+ * If destination buffer or length is not 16-byte aligned then:
+ * - decrypt portion of destination buffer into intermediate buffer
+ * (dst_tpage)
+ * - copy the source data into intermediate buffer
+ * - use the intermediate buffer as source buffer
+ */
+
+ /* If source is not aligned (case 1) */
+ if (!IS_ALIGNED(vaddr, 16)) {
+ src_tpage = alloc_page(GFP_KERNEL);
+ if (!src_tpage)
+ return -ENOMEM;
+
+ if (copy_from_user(page_address(src_tpage),
+ (uint8_t *)vaddr, size)) {
+ __free_page(src_tpage);
+ return -EFAULT;
+ }
+ paddr = __sme_page_pa(src_tpage);
+
+ /* flush the caches to ensure that DRAM has recent contents */
+ clflush_cache_range(page_address(src_tpage), PAGE_SIZE);
+ }
+
+ /* If destination buffer or length is not aligned (case 2) */
+ if (!IS_ALIGNED(dst_vaddr, 16) || !IS_ALIGNED(size, 16)) {
+ int dst_offset;
+
+ dst_tpage = alloc_page(GFP_KERNEL);
+ if (!dst_tpage) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+
+ /* decrypt destination buffer into intermediate buffer */
+ ret = __sev_dbg_dec(kvm,
+ round_down(dst_paddr, 16),
+ 0,
+ (unsigned long)page_address(dst_tpage),
+ __sme_page_pa(dst_tpage),
+ round_up(size, 16),
+ error);
+ if (ret)
+ goto e_free;
+
+ dst_offset = dst_paddr & 15;
+
+ /*
+ * modify the intermediate buffer with data from source
+ * buffer.
+ */
+ if (src_tpage)
+ memcpy(page_address(dst_tpage) + dst_offset,
+ page_address(src_tpage), size);
+ else {
+ if (copy_from_user(page_address(dst_tpage) + dst_offset,
+ (void *) vaddr, size)) {
+ ret = -EFAULT;
+ goto e_free;
+ }
+ }
+
+
+ /* use intermediate buffer as source */
+ paddr = __sme_page_pa(dst_tpage);
+
+ /* flush the caches to ensure that DRAM gets recent updates */
+ clflush_cache_range(page_address(dst_tpage), PAGE_SIZE);
+
+ /* now we have length and destination buffer aligned */
+ dst_paddr = round_down(dst_paddr, 16);
+ len = round_up(size, 16);
+ }
+
+ ret = __sev_dbg_enc_dec(kvm, paddr, dst_paddr, len, error, true);
+e_free:
+ if (src_tpage)
+ __free_page(src_tpage);
+ if (dst_tpage)
+ __free_page(dst_tpage);
+ return ret;
+}
+
+static int sev_dbg_encrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ unsigned long vaddr, vaddr_end, dst_vaddr, next_vaddr;
+ struct kvm_sev_dbg debug;
+ int ret, size;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ if (copy_from_user(&debug, (void *)argp->data,
+ sizeof(struct kvm_sev_dbg)))
+ return -EFAULT;
+
+ size = debug.length;
+ vaddr = debug.src_addr;
+ vaddr_end = vaddr + size;
+ dst_vaddr = debug.dst_addr;
+
+ for (; vaddr < vaddr_end; vaddr = next_vaddr) {
+ unsigned long n;
+ int s_off, d_off, len;
+ struct page **srcpage, **dstpage;
+
+ /* lock the user memory */
+ srcpage = sev_pin_memory(vaddr & PAGE_MASK, PAGE_SIZE, &n, 0);
+ if (!srcpage)
+ return -EFAULT;
+
+ dstpage = sev_pin_memory(dst_vaddr & PAGE_MASK, PAGE_SIZE,
+ &n, 1);
+ if (!dstpage) {
+ sev_unpin_memory(srcpage, n);
+ return -EFAULT;
+ }
+
+ /* flush the caches to ensure that DRAM has recent contents */
+ sev_clflush_pages(srcpage, 1);
+ sev_clflush_pages(dstpage, 1);
+
+ /*
+ * since user buffer may not be page aligned, calculate the
+ * offset within the page.
+ */
+ s_off = vaddr & ~PAGE_MASK;
+ d_off = dst_vaddr & ~PAGE_MASK;
+ len = min_t(size_t, (PAGE_SIZE - s_off), size);
+
+ ret = __sev_dbg_enc(kvm,
+ vaddr,
+ __sme_page_pa(srcpage[0]) + s_off,
+ dst_vaddr,
+ __sme_page_pa(dstpage[0]) + d_off,
+ len, &argp->error);
+
+ /* unlock the user memory */
+ sev_unpin_memory(srcpage, 1);
+ sev_unpin_memory(dstpage, 1);
+
+ if (ret)
+ goto err;
+
+ next_vaddr = vaddr + len;
+ dst_vaddr = dst_vaddr + len;
+ size -= len;
+ }
+err:
+ return ret;
+}
+
static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
{
struct kvm_sev_cmd sev_cmd;
@@ -6253,6 +6423,10 @@ static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
r = sev_dbg_decrypt(kvm, &sev_cmd);
break;
}
+ case KVM_SEV_DBG_ENCRYPT: {
+ r = sev_dbg_encrypt(kvm, &sev_cmd);
+ break;
+ }
default:
break;
}
--
2.9.4
The command is used for decrypting a guest memory region for debug
purposes.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 160 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 21f85e1..933384a 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -6058,6 +6058,162 @@ static int sev_guest_status(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int __sev_dbg_enc_dec(struct kvm *kvm, unsigned long src,
+ unsigned long dst, int size, int *error, bool enc)
+{
+ struct sev_data_dbg *data;
+ int ret;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ data->handle = sev_get_handle(kvm);
+ data->dst_addr = dst;
+ data->src_addr = src;
+ data->length = size;
+
+ ret = sev_issue_cmd(kvm,
+ enc ? SEV_CMD_DBG_ENCRYPT : SEV_CMD_DBG_DECRYPT,
+ data, error);
+ kfree(data);
+ return ret;
+}
+
+/*
+ * Decrypt source memory into userspace or kernel buffer. If destination buffer
+ * or len is not aligned to 16-byte boundary then it uses intermediate buffer.
+ */
+static int __sev_dbg_dec(struct kvm *kvm, unsigned long paddr,
+ unsigned long __user dst_uaddr,
+ unsigned long dst_kaddr, unsigned long dst_paddr,
+ int size, int *error)
+{
+ int ret, offset, len = size;
+ struct page *tpage = NULL;
+
+ /*
+ * Debug command works with 16-byte aligned inputs, check if all inputs
+ * (src, dst and len) are 16-byte aligned. If one of the input is not
+ * aligned then we decrypt more than requested into a temporary buffer
+ * and copy the porition of data into destination buffer.
+ */
+ if (!IS_ALIGNED(paddr, 16) || !IS_ALIGNED(dst_paddr, 16) ||
+ !IS_ALIGNED(size, 16)) {
+ tpage = (void *)alloc_page(GFP_KERNEL);
+ if (!tpage)
+ return -ENOMEM;
+
+ dst_paddr = __sme_page_pa(tpage);
+
+ /*
+ * if source buffer is not aligned then offset will be used
+ * when copying the data from the temporary buffer into
+ * destination buffer.
+ */
+ offset = paddr & 15;
+
+ /* its safe to read more than requested size. */
+ len = round_up(size + offset, 16);
+
+ paddr = round_down(paddr, 16);
+ }
+
+ ret = __sev_dbg_enc_dec(kvm, paddr, dst_paddr, len, error, false);
+ /*
+ * If temporary buffer is used then copy the data from temporary buffer
+ * into destination buffer.
+ */
+ if (tpage) {
+
+ /*
+ * If destination buffer is a userspace buffer then use
+ * copy_to_user otherwise memcpy.
+ */
+ if (dst_uaddr) {
+ if (copy_to_user((uint8_t *)dst_uaddr,
+ page_address(tpage) + offset, size))
+ ret = -EFAULT;
+ } else {
+ memcpy((void *)dst_kaddr,
+ page_address(tpage) + offset, size);
+ }
+
+ __free_page(tpage);
+ }
+
+ return ret;
+}
+
+static int sev_dbg_decrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ unsigned long vaddr, vaddr_end, next_vaddr;
+ unsigned long dst_vaddr, dst_vaddr_end;
+ struct page **srcpage, **dstpage;
+ struct kvm_sev_dbg debug;
+ unsigned long n;
+ int ret, size;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ if (copy_from_user(&debug, (void *)argp->data,
+ sizeof(struct kvm_sev_dbg)))
+ return -EFAULT;
+
+ vaddr = debug.src_addr;
+ size = debug.length;
+ vaddr_end = vaddr + size;
+ dst_vaddr = debug.dst_addr;
+ dst_vaddr_end = dst_vaddr + size;
+
+ for (; vaddr < vaddr_end; vaddr = next_vaddr) {
+ int len, s_off, d_off;
+
+ /* lock userspace source and destination page */
+ srcpage = sev_pin_memory(vaddr & PAGE_MASK, PAGE_SIZE, &n, 0);
+ if (!srcpage)
+ return -EFAULT;
+
+ dstpage = sev_pin_memory(dst_vaddr & PAGE_MASK, PAGE_SIZE,
+ &n, 1);
+ if (!dstpage) {
+ sev_unpin_memory(srcpage, n);
+ return -EFAULT;
+ }
+
+ /* flush the caches to ensure that DRAM has recent contents */
+ sev_clflush_pages(srcpage, 1);
+ sev_clflush_pages(dstpage, 1);
+
+ /*
+ * since user buffer may not be page aligned, calculate the
+ * offset within the page.
+ */
+ s_off = vaddr & ~PAGE_MASK;
+ d_off = dst_vaddr & ~PAGE_MASK;
+ len = min_t(size_t, (PAGE_SIZE - s_off), size);
+
+ ret = __sev_dbg_dec(kvm,
+ __sme_page_pa(srcpage[0]) + s_off,
+ dst_vaddr, 0,
+ __sme_page_pa(dstpage[0]) + d_off,
+ len, &argp->error);
+
+ sev_unpin_memory(srcpage, 1);
+ sev_unpin_memory(dstpage, 1);
+
+ if (ret)
+ goto err;
+
+ next_vaddr = vaddr + len;
+ dst_vaddr = dst_vaddr + len;
+ size -= len;
+ }
+err:
+ return ret;
+}
+
static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
{
struct kvm_sev_cmd sev_cmd;
@@ -6093,6 +6249,10 @@ static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
r = sev_guest_status(kvm, &sev_cmd);
break;
}
+ case KVM_SEV_DBG_DECRYPT: {
+ r = sev_dbg_decrypt(kvm, &sev_cmd);
+ break;
+ }
default:
break;
}
--
2.9.4
On AMD platform, under certain conditions insn_len may be zero on #NPF.
This can happen if guest gets a page-fault on data access, but HW table
walker is not able to read the instruction page (e.g instuction page
is not present in memory).
Typically, when insn_len is zero, x86_emulate_instruction() walks the
guest page table and fetches the instruction bytes from guest memory.
When SEV is enabled, the guest memory is encrypted with guest-specific
key hence hypervisor will not able to fetch the instruction bytes.
In those cases we simply restart the guest.
I have encountered this issue when running kernbench inside the guest.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/mmu.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index ccb70b8..be41ad0 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -4850,6 +4850,23 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
if (mmio_info_in_cache(vcpu, cr2, direct))
emulation_type = 0;
emulate:
+ /*
+ * On AMD platform, under certain conditions insn_len may be zero on #NPF.
+ * This can happen if guest gets a page-fault on data access, but HW table
+ * walker is not able to read the instruction page (e.g instuction page
+ * is not present).
+ *
+ * Typically, when insn_len is zero, x86_emulate_instruction() walks the
+ * guest page table and fetches the instruction bytes. When SEV is active,
+ * the guest memory is encrypted with guest key hence we will not able to
+ * fetch the instruction bytes. In those cases we simply restart the guest.
+ */
+ if (unlikely(!insn_len)) {
+ if (kvm_x86_ops->memory_encryption_enabled &&
+ kvm_x86_ops->memory_encryption_enabled(vcpu))
+ return 1;
+ }
+
er = x86_emulate_instruction(vcpu, cr2, emulation_type, insn, insn_len);
switch (er) {
--
2.9.4
On #UD, x86_emulate_instruction() fetches the data from guest memory and
decodes the instruction bytes to assist further. When SEV is enabled, the
instruction bytes will be encrypted using the guest-specific key, hypervisor
will no longer able to fetch the instruction bytes to assist UD handling.
By not installing intercept we let the guest receive and handle #UD.
Signed-off-by: Brijesh Singh <[email protected]>
---
arch/x86/kvm/svm.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 64b9f60..4581d03 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -1432,8 +1432,10 @@ static void init_vmcb(struct vcpu_svm *svm)
svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK;
}
- if (sev_guest(svm->vcpu.kvm))
+ if (sev_guest(svm->vcpu.kvm)) {
svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE;
+ clr_exception_intercept(svm, UD_VECTOR);
+ }
mark_all_dirty(svm->vmcb);
--
2.9.4
Hi,
minor misspelling,
On 24.07.2017 22:02, Brijesh Singh wrote:
> Platform Security Processor (PSP) is part of AMD Secure Processor (AMD-SP),
> PSP is a dedicated processor that provides the support for key management
> commands in a Secure Encrypted Virtualiztion (SEV) mode, along with
> software-based Tursted Executation Environment (TEE) to enable the
----------------- ^ Trusted
> third-party tursted applications.
-------------- ^ trusted
[...]
--
Best regards,
Kamil Konieczny
Samsung R&D Institute Poland
On 07/25/2017 03:29 AM, Kamil Konieczny wrote:
> Hi,
>
> minor misspelling,
>
> On 24.07.2017 22:02, Brijesh Singh wrote:
>> Platform Security Processor (PSP) is part of AMD Secure Processor (AMD-SP),
>> PSP is a dedicated processor that provides the support for key management
>> commands in a Secure Encrypted Virtualiztion (SEV) mode, along with
>> software-based Tursted Executation Environment (TEE) to enable the
> ----------------- ^ Trusted
>> third-party tursted applications.
> -------------- ^ trusted
> [...]
>
Noted. thanks
-Brijesh
On Mon, Jul 24, 2017 at 03:02:38PM -0500, Brijesh Singh wrote:
> Create a Documentation entry to describe the AMD Secure Encrypted
> Virtualization (SEV) feature.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> .../virtual/kvm/amd-memory-encryption.txt | 328 +++++++++++++++++++++
> 1 file changed, 328 insertions(+)
> create mode 100644 Documentation/virtual/kvm/amd-memory-encryption.txt
>
> diff --git a/Documentation/virtual/kvm/amd-memory-encryption.txt b/Documentation/virtual/kvm/amd-memory-encryption.txt
> new file mode 100644
> index 0000000..cffed2d
> --- /dev/null
> +++ b/Documentation/virtual/kvm/amd-memory-encryption.txt
You need to add this new file to Documentation/virtual/kvm/00-INDEX
> @@ -0,0 +1,328 @@
> +Secure Encrypted Virtualization (SEV) is a feature found on AMD processors.
> +
> +SEV is an extension to the AMD-V architecture which supports running virtual
> +machine (VMs) under the control of a hypervisor. When enabled, the memory
"machines"
> +contents of VM will be transparently encrypted with a key unique to the VM.
> +
> +Hypervisor can determine the SEV support through the CPUID instruction. The CPUID
> +function 0x8000001f reports information related to SEV:
> +
> + 0x8000001f[eax]:
> + Bit[1] indicates support for SEV
> +
> + 0x8000001f[ecx]:
0x8000001f[eax]:
Bit[1]
... [ecx]:
Bits[31:0]
looks more compact to me and shows quicker that it is the same CPUID
leaf, just different reg.
While at it, you can do that to
Documentation/x86/amd-memory-encryption.txt too and now that I look at
it, 0x800001f[eax]: is short one 0.
> + Bits[31:0] Number of encrypted guest supported simultaneously
guests
> +
> +If support for SEV is present, MSR 0xc00100010 (MSR_K8_SYSCFG) and MSR
0xc0010010
there's one 0 too many in yours. Also, write it 0xc001_0010, with the
4-digit help bar.
> +0xc0000015 (MSR_K7_HWCR_SMMLOCK) can be used to determine if it can be enabled:
Do you mean 0xc0010015 (MSR_K7_HWCR) here? Because the MSR is
#define MSR_K7_HWCR 0xc0010015
That MSR_K7_HWCR_SMMLOCK is bit 0 in it.
> +
> + 0xc00100010:
that's 9 hex digits
> + Bit[23] 0 = memory encryption can be enabled
> + 0 = memory encryption can not be enabled
^-- one of those needs to be 1b :-)
> +
> + 0xc00010015:
Ditto.
> + Bit[0] 0 = memory encryption can not be enabled
> + 1 = memory encryption can be enabled
> +
> +When SEV support is available, it can be enabled on specific VM during the VMRUN
s/on/in a/
and not "during the VMRUN... " but say "by setting SEV bit ... before
executing VMRUN."
> +instruction by setting SEV bit in VMCB offset 090h:
> +
> + VMCB offset 090h:
I guess
VMCB[0x90]
?
> + Bit[1] 1 = Enable SEV
> +
> +SEV hardware uses ASIDs to associate memory encryption key with the guest VMs.
"...to associate a memory encryption key with a VM."
> +Hence the ASID for the SEV-enabled guests must be from 1 to a maximum value
"Hence, ..."
> +defined through the CPUID function 0x8000001f[ECX].
"defined in the CPUID ... field."
Also, s/ECX/ecx/ as you're using small letters for register names
consistently so far.
> +
> +
> +SEV Key Management
> +------------------
> +
> +The Key management for the SEV guest is handled by a seperate processor known as
WARNING: 'seperate' may be misspelled - perhaps 'separate'?
#74: FILE: Documentation/virtual/kvm/amd-memory-encryption.txt:41:
+The Key management for the SEV guest is handled by a seperate processor known as
Run them all through a spellchecker pls.
> +the AMD Secure Processor (AMD-SP). Firmware running inside the AMD-SP provides a
> +secure key management interface to perform common hypervisor activities such as
> +encrypting bootstrap code, snapshotting, migrating and debugging the guest. For
> +more informaiton, see SEV Key Management spec:
^^^^^^^^^^^
This looks misspelled too but I caught it and not checkpatch!
Meh, what good is that thing - it can't even catch all typos?! :-\
> +
> +http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
<--- here you can add an introductory sentence or two:
"KVM implements the following commands to support SEV guests... " and so on.
Also, the userspace API is documented in
Documentation/virtual/kvm/api.txt. Shouldn't those be there too, to have
them in one place?
> +
> +1. KVM_SEV_LAUNCH_START
> +
> +Parameters: struct kvm_sev_launch_start (in/out)
> +Returns: 0 on success, -negative on error
> +
> +LAUNCH_START command is used to bootstrap a guest by encrypting its memory with
"The KVM_SEV_LAUNCH_START command ... "
> +a new VM Encryption Key (VEK). In order to create guest context, hypervisor should
the
You need to start using (in-)definite articles in your sentences - text reads
strange now. Or should I say *the* text reads strange now? :-)
> +provide guest policy, owners public diffie-hellman (PDH) key and session parameters.
^ ^ ^
a the owner's Diffie-Hellman
or owners'. There are more occurrences of this below.
> +
> +The guest policy constrains the use and features activated for the lifetime of the
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Please rewrite that - I can only try guessing what it means.
> +launched guest, such as disallowing debugging, enabling key sharing, or turning on
> +other SEV related features.
"SEV-related"
> +
> +The guest owners PDH allows the firmware to establish a cryptographic session with
> +the guest owner to negotiate keys used for attestation.
^
in order to
> +
> +The session parameters contains informations such as guest policy MAC, transport
WARNING: 'informations' may be misspelled - perhaps 'information'?
#98: FILE: Documentation/virtual/kvm/amd-memory-encryption.txt:65:
+The session parameters contains informations such as guest policy MAC, transport
also s/contains/contain/
> +integrity key (TIK), transport encryption key (TEK) etc.
> +
> +struct kvm_sev_launch_start {
> +
> + /* Guest Hanldle, if zero then FW creates a new handle */
> + __u32 handle;
> +
> + /* Guest policy */
> + __u32 policy;
> +
> + /* Address which contains guest owner's PDH certificate blob */
> + __u64 dh_cert_address;
> + __u32 dh_cert_length;
> +
> + /* Address which contains guest session information blob */
> + __u64 session_address;
> + __u32 session_length;
> +};
> +
> +On success, the 'handle' field contain a new handle.
... and on error, a negative value...
> +
> +2. KVM_SEV_LAUNCH_UPDATE_DATA
> +
> +Parameters (in): struct kvm_sev_launch_update
> +Returns: 0 on success, -negative on error
<--- here you need to explain first what the command does and what could
be used for and then how it does it.
> +LAUNCH_UPDATE_DATA encrypts the memory region using the VEK created during
To avoid confusion, use the "KVM_SEV_"-prefixed defines pls.
> +LAUNCH_START. It also calculates a measurement of the memory region. This
> +measurement can be used as a signature of the memory contents.
> +
> +struct kvm_sev_launch_update {
> + /* address of the data to be encrypted (must be 16-byte aligned) */
> + __u64 address;
> +
> + /* length of the data to be encrypted (must be 16-byte aligned) */
> + __u32 length;
> +};
> +
> +3. KVM_SEV_LAUNCH_MEASURE
> +
> +Parameters (in): struct kvm_sev_launch_measure
> +Returns: 0 on success, -negative on error
> +
> +LAUNCH_MEASURE returns the measurement of the memory region encrypted with
> +LAUNCH_UPDATE_DATA. The measurement is keyed with the TIK so that the guest
> +owner can use the measurement to verify the guest was properly launched without
> +tempering.
So this could use a bit more text as it is such an important aspect of
the whole verification of the guest.
> +
> +struct kvm_sev_launch_measure {
> + /* where to copy the measurement blob */
> + __u64 address;
> +
> + /* length of memory region containing measurement */
> + __u32 length;
> +};
> +
> +If measurement length is too small, the required length is returned in the
> +length field.
> +
> +On success, the measurement is copied to the address.
And how is success signalled to the caller?
> +
> +4. KVM_SEV_LAUNCH_FINISH
> +
> +Returns: 0 on success, -negative on error
> +
> +LAUNCH_FINISH command finalize the SEV guest launch process.
"The KVM_SEV_LAUNCH_FINISH command... "
> +
> +5. KVM_SEV_GUEST_STATUS
> +
> +Parameters (out): struct kvm_sev_guest_status
This is an "out" command, so it should be called
KVM_SEV_GET_GUEST_STATUS. Or is it too late for that?
> +Returns: 0 on success, -negative on error
> +
> +GUEST_STATUS returns the current SEV state the guest is in.
> +
> +struct kvm_sev_guest_status {
> +
> + /* guest hanldle */
> + __u32 handle;
> +
> + /* guest policy */
> + __u32 policy;
> +
> + /* guest state (see below) */
> + __u8 state;
> +};
> +
> +SEV guest state:
> +
> +enum {
> + /* guest state is not known */
> + SEV_STATE_INVALID = 0;
not known or invalid?
> + /* guest is currently being launched */
> + SEV_STATE_LAUNCHING.
^--- comma, I guess, instead of full-stop.
> + /* guest is being launched and ready to accept the ciphertext data */
> + SEV_STATE_SECRET,
> + /* guest is fully launched and running */
> + SEV_STATE_RUNNING,
> + /* guest is being migrated in from another SEV machine */
> + SEV_STATE_RECEIVING,
> + /* guest is getting migrated out another SEV machine */
"out to another"
> + SEV_STATE_SENDING
> +};
Btw, side-comments will make this much more readable:
enum {
SEV_STATE_INVALID = 0,
SEV_STATE_LAUNCHING,
SEV_STATE_SECRET, /* guest is being launched and ready to accept the ciphertext data */
SEV_STATE_RUNNING, /* guest is fully launched and running */
SEV_STATE_RECEIVING, /* guest is being migrated in from another SEV machine */
SEV_STATE_SENDING, /* guest is getting migrated out to another SEV machine */
};
> +
> +6. KVM_SEV_DBG_DECRYPT
> +
> +DEBUG_DECRYPT command can be used for decrypting a region of guest memory for
> +the SEV guest debug purposes. Note that since decrypting protected memory allows
Now here is "the" wrong. Ditto below.
Also, what is "protected memory"? You mean "guest memory", right?
> +the hypervisor to gain access to guest memory, the guest policy must explicitly
> +allow debugging for this command to work.
> +
> +Parameters (in): struct kvm_sev_dbg
> +Returns: 0 on success, -negative on error
> +
> +struct kvm_sev_dbg {
> + __u64 src_address;
> + __u64 dst_address;
Even though obvious, those need comments what they are, just like the
other struct members above. Below need comments too.
> +
> + /* length of memory region to decrypt */
> + __u32 length;
> +};
> +
> +7. KVM_SEV_DBG_ENCRYPT
> +
> +DEBUG_ENCRYPT command can be used for injecting the data into guest for the SEV
"The ... " - but you get the idea :)
make that "... can be used for injecting data into a guest for debugging
purposes."
> +guest debug purposes. Note that since injecting the data into protected memory
> +allows the hypervisor to modify the guest memory, the guest policy must explicitly
> +allow debugging for this command to work.
Same issues as above.
> +
> +Parameters (in): struct kvm_sev_dbg
> +Returns: 0 on success, -negative on error
> +
> +struct kvm_sev_dbg {
> + __u64 src_address;
> + __u64 dst_address;
> +
> + /* length of memory region to encrypt */
> + __u32 length;
> +};
> +
> +8. KVM_SEV_SEND_START
> +
> +Parameters (in): struct kvm_sev_send_start
> +Returns: 0 on success, -negative on error
> +
> +SEND_START command is used to export a SEV guest from one platform to another.
Export or migrate?
> +It can be used for saving a guest to disk to be resumed later, or it can be
> +used to migrate a guest across the network to a receiving platform.
And how do I specify which of those actions needs to happen?
> +
> +struct kvm_sev_send_start {
> + /* guest policy */
> + __u32 policy;
> +
> + /* address which contains receivers PDH key blob */
the receiver's
> + __u64 pdh_cert_address;
> + __u32 pdh_cert_length;
> +
> + /* address which contains platform certificate blob */
> + __u64 plat_cert_address;
> + __u32 plat_cert_length;
> +
> + /* address which contains AMD certificate chain */
> + __u64 amd_cert_address;
> + __u32 amd_cert_length;
> +
> + /* where to copy the current session information */
> + __u64 session_address;
> + __u32 session_length;
> +};
> +
> +The command uses PDH key to establish a new cryptographic context with the
> +remote platform - the new cryptographic context will be used for re-encrypting
> +the guest memory before sending it to remote platform.
> +
> +If length of the certificate blobs are too small, the required length is
So you wanna say "If the certificate blobs are short, ... "
> +returned in the length field and an error is returned.
> +
> +9. KVM_SEV_SEND_UPDATE_DATA
> +
> +Parameters (in): struct kvm_sev_send_update_data
> +Returns: 0 on success, -negative on error
> +
> +SEND_UPDATE_DATA command is used to re-encrypt the guest memory using the
> +crytographic context established during SEND_START. A fresh IV is generated
> +and written to the packet header field.
> +
> +struct kvm_sev_send_update_data {
> + /* address which will contain packet header (IV, MAC etc)*/
> + __u64 hdr_data;
> + __u32 hdr_length;
> +
> + /* address of guest memory region containg encrypted data */
"containing" - spellchecker needed.
> + __u64 guest_address;
> + __u32 guest_length;
> +
> + /* address of transport buffer */
> + __u64 host_address;
> + __u32 host_length;
> +};
> +
> +If the hdr_length is too small, the required length is returned in the length
> +field and an error is returned.
> +
> +10. KVM_SEV_SEND_FINISH
> +
> +Returns: 0 on success, -negative on error
> +
> +SEND_FINISH command finalize the SEV guest sending process.
"finalizes"
> +
> +11. KVM_SEV_RECEIVE_START
> +
> +Parameters (in): struct kvm_sev_receive_start
> +Returns: 0 on success, -negative on error
> +
> +RECEIVE_START command is used to import a guest from one platform to another.
> +It can be used for restoring a guest from disk, or it can be used to migrate
> +a guest across the network from a sending platform.
Same issues as above.
Also, the explanatory text doesn't say who calls that command. If it is
called, KVM_SEV_RECEIVE_START, so it must be the receiving end but it is
unclear.
> +
> +struct kvm_sev_receive_start {
> + /* guest handle (if zero then new handle will be created) */
> + __u32 handle;
> +
> + /* guest policy */
> + __u32 policy;
> +
> + /* Address containing senders PDH certificate blob */
> + __u64 pdh_cert_address;
> + __u32 pdh_cert_length;
> +
> + /* Address containing sender's session information blob */
> + __u64 session_address;
> + __u32 session_length;
> +};
> +
> +The RECEIVE_START command creates a new cryptographic context necessary to
> +re-enrypt the guest memory receieved through the RECEIVE_UPDATE command.
"re-encrypt" - typo. Also "received"
Also, what is the RECEIVE_UPDATE command? The
KVM_SEV_RECEIVE_UPDATE_DATA below? See what I mean with ambiguities.
> +
> +12. KVM_SEV_RECEIVE_UPDATE_DATA
> +
> +Parameters (in): struct kvm_sev_receive_update_data
> +Returns: 0 on success, -negative on error
> +
> +RECEIVE_UPDATE_DATA command is used to re-encrypt the guest memory using the
> +crytographic context established during RECEIVE_START.
> +
> +struct kvm_sev_receive_update_data {
> + /* packet header receieved from the SEND_UPDATE_DATA command */
> + __u64 hdr_data;
> + __u32 hdr_length;
> +
> + /* address of guest memory region */
> + __u64 guest_address;
> + __u32 guest_length;
> +
> + /* address of transport buffer */
> + __u64 host_address;
> + __u32 host_length;
> +};
> +
> +13. KVM_SEV_RECEIVE_FINISH
> +
> +Returns: 0 on success, -negative on error
> +
> +RECEIVE_FINISH command finalize the SEV guest receiving process.
Also, "finalizes".
Phew, that took long.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
Hi Boris,
Thanks for detail review, I have incorporate the spell check
in my work flow and will be fixing all those spell check errors
innext rev.
On 09/05/2017 12:21 PM, Borislav Petkov wrote:
[...]
>> +3. KVM_SEV_LAUNCH_MEASURE
>> +
>> +Parameters (in): struct kvm_sev_launch_measure
>> +Returns: 0 on success, -negative on error
>> +
>> +LAUNCH_MEASURE returns the measurement of the memory region encrypted with
>> +LAUNCH_UPDATE_DATA. The measurement is keyed with the TIK so that the guest
>> +owner can use the measurement to verify the guest was properly launched without
>> +tempering.
>
> So this could use a bit more text as it is such an important aspect of
> the whole verification of the guest.
>
>> +
>> +struct kvm_sev_launch_measure {
>> + /* where to copy the measurement blob */
>> + __u64 address;
>> +
>> + /* length of memory region containing measurement */
>> + __u32 length;
>> +};
>> +
>> +If measurement length is too small, the required length is returned in the
>> +length field.
>> +
>> +On success, the measurement is copied to the address.
>
> And how is success signalled to the caller?
>
The measurement verification is performed outside the KVM/Qemu.
From driver point of view, all we have to do is issues LAUNCH_MEASURE
command when userspace asks for the measurement. I can see that command
name is confusing - I am thinking of renaming it to
"KVM_SEV_GET_LAUNCH_MEASUREMENT"
The complete flow is listed in Appendix A of SEV firmware spec [1].
I will update the doc to give SEV spec section references for the details.
Not sure if we need to document the complete measurement flow in the
driver doc.
[...]
>> +
>> +4. KVM_SEV_LAUNCH_FINISH
>> +
>> +Returns: 0 on success, -negative on error
>> +
>> +LAUNCH_FINISH command finalize the SEV guest launch process.
>
> "The KVM_SEV_LAUNCH_FINISH command..."
>
>> +
>> +5. KVM_SEV_GUEST_STATUS
>> +
>> +Parameters (out): struct kvm_sev_guest_status
>
> This is an "out" command, so it should be called
> KVM_SEV_GET_GUEST_STATUS. Or is it too late for that?
I was trying map with SEV firmware spec command names but I see your
point and will call it "KVM_SEV_GET_GUEST_STATUS".
>> +
>> +enum {
>> + /* guest state is not known */
>> + SEV_STATE_INVALID = 0;
>
> not known or invalid?
Again, was trying to follow the spec naming convention but I can go
with UNKNOWN ..
>
> Btw, side-comments will make this much more readable:
>
> enum {
> SEV_STATE_INVALID = 0,
> SEV_STATE_LAUNCHING,
> SEV_STATE_SECRET, /* guest is being launched and ready to accept the ciphertext data */
> SEV_STATE_RUNNING, /* guest is fully launched and running */
> SEV_STATE_RECEIVING, /* guest is being migrated in from another SEV machine */
> SEV_STATE_SENDING, /* guest is getting migrated out to another SEV machine */
> };
>
I was trying to keep everything to 80 column limit but if that is
not an issue for documentation then I like your recommendation.
[...]
>> +8. KVM_SEV_SEND_START
>> +
>> +Parameters (in): struct kvm_sev_send_start
>> +Returns: 0 on success, -negative on error
>> +
>> +SEND_START command is used to export a SEV guest from one platform to another.
>
> Export or migrate?
>
>> +It can be used for saving a guest to disk to be resumed later, or it can be
>> +used to migrate a guest across the network to a receiving platform.
>
> And how do I specify which of those actions needs to happen?
>
The command does not require explicit parameter to differentiate between
live migration vs snapshot. All it needs is a destination platform
PDH key. If its live migration case then VM management stack will probably
communicate with remote platform and get its PDH keys before calling us.
The KVM driver simply acts upon the request from the userspace. SEV firmware
spec Appendix A [1] provides complete flow diagram which need to be implemented
in userspace. The driver simply act upon when it asked to create SEND_START
context.
[1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
>
> Phew, that took long.
>
Thank you for detail review.
On Tue, Sep 05, 2017 at 04:39:14PM -0500, Brijesh Singh wrote:
> I was trying map with SEV firmware spec command names but I see your
> point and will call it "KVM_SEV_GET_GUEST_STATUS".
>
>
> > > +
> > > +enum {
> > > + /* guest state is not known */
> > > + SEV_STATE_INVALID = 0;
> >
> > not known or invalid?
>
>
> Again, was trying to follow the spec naming convention but I can go
> with UNKNOWN ..
Yeah, but they will now differ from the spec, which weakens my point
considerably. I guess using KVM_SEV_<spec_name> everywhere is the
optimal solution for the commands and the SEV_STATE_<spec_name> for the
states.
Because having them differ from the spec - esp. for the sake of some
more precise naming - is worse. IMO, of course.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Tue, Sep 05, 2017 at 04:39:14PM -0500, Brijesh Singh wrote:
> Not sure if we need to document the complete measurement flow in the
> driver doc.
No, not the whole thing - only summarized in a couple of sentences with
the link to the doc.
> I was trying to keep everything to 80 column limit but if that is
> not an issue for documentation then I like your recommendation.
That rule is not a hard one - rather, it is to human discretion what
is better - readability or fitting on some small screen, no one uses
anymore.
> The command does not require explicit parameter to differentiate between
> live migration vs snapshot. All it needs is a destination platform
> PDH key. If its live migration case then VM management stack will probably
> communicate with remote platform and get its PDH keys before calling us.
> The KVM driver simply acts upon the request from the userspace. SEV firmware
> spec Appendix A [1] provides complete flow diagram which need to be implemented
> in userspace. The driver simply act upon when it asked to create SEND_START
> context.
Ok, so that only creates the context after sending the PDH cert into the
firmware. So please state that first and then what the command can be
used for. The way it is written now, it reads like it does the sending
of the guest.
Thx.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:39PM -0500, Brijesh Singh wrote:
> Platform Security Processor (PSP) is part of AMD Secure Processor (AMD-SP),
> PSP is a dedicated processor that provides the support for key management
> commands in a Secure Encrypted Virtualiztion (SEV) mode, along with
> software-based Tursted Executation Environment (TEE) to enable the
> third-party tursted applications.
>
> Cc: Herbert Xu <[email protected]>
> Cc: David S. Miller <[email protected]>
> Cc: Gary Hook <[email protected]>
> Cc: [email protected]
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> drivers/crypto/ccp/Kconfig | 9 ++
> drivers/crypto/ccp/Makefile | 1 +
> drivers/crypto/ccp/psp-dev.c | 226 +++++++++++++++++++++++++++++++++++++++++++
> drivers/crypto/ccp/psp-dev.h | 82 ++++++++++++++++
> drivers/crypto/ccp/sp-dev.c | 43 ++++++++
> drivers/crypto/ccp/sp-dev.h | 41 +++++++-
> drivers/crypto/ccp/sp-pci.c | 46 +++++++++
> 7 files changed, 447 insertions(+), 1 deletion(-)
> create mode 100644 drivers/crypto/ccp/psp-dev.c
> create mode 100644 drivers/crypto/ccp/psp-dev.h
...
> diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c
> index a017233..d263ba4 100644
> --- a/drivers/crypto/ccp/sp-dev.c
> +++ b/drivers/crypto/ccp/sp-dev.c
Hunk #1 succeeded at 24 (offset -7 lines).
checking file drivers/crypto/ccp/Makefile
Hunk #1 FAILED at 7.
1 out of 1 hunk FAILED
checking file drivers/crypto/ccp/psp-dev.c
checking file drivers/crypto/ccp/psp-dev.h
can't find file to patch at input line 414
Perhaps you used the wrong -p or --strip option?
The text leading up to this was:
--------------------------
|diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c
|index a017233..d263ba4 100644
|--- a/drivers/crypto/ccp/sp-dev.c
|+++ b/drivers/crypto/ccp/sp-dev.c
--------------------------
What tree is that against? In any case, it doesn't apply here.
> This RFC is based on tip/master commit : 22db3de (Merge branch 'x86/mm').
$ git show 22db3de
fatal: ambiguous argument '22db3de': unknown revision or path not in the working tree.
Do you have updated version of the series which you can send out?
> @@ -67,6 +74,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 *);
WARNING: missing space after return type
#502: FILE: drivers/crypto/ccp/sp-dev.h:79:
+ void(*set_psp_master_device)(struct sp_device *);
Don't forget to run all patches through checkpatch. Some of the warnings
make sense.
Thx.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
Hi Boris,
On 09/06/2017 12:00 PM, Borislav Petkov wrote:
...
> --------------------------
> |diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c
> |index a017233..d263ba4 100644
> |--- a/drivers/crypto/ccp/sp-dev.c
> |+++ b/drivers/crypto/ccp/sp-dev.c
> --------------------------
>
> What tree is that against? In any case, it doesn't apply here.
>
>> This RFC is based on tip/master commit : 22db3de (Merge branch 'x86/mm').
>
This bit of my struggle -- tip/master is not in sync with cryptodev-2.6 [1].
In order to expand the CCP driver we need the following commits from the
cryptodev-2.6
57de3aefb73f crypto: ccp - remove ccp_present() check from device initialize
d0ebbc0c407a crypto: ccp - rename ccp driver initialize files as sp device
f4d18d656f88 crypto: ccp - Abstract interrupt registeration
720419f01832 crypto: ccp - Introduce the AMD Secure Processor device
970e8303cb8d crypto: ccp - Use devres interface to allocate PCI/iomap and cleanup
I cherry-picked these patches into tip/master before starting the SEV work.
Since these patches were already reviewed and accepted hence I did not include it
in my RFC series. I am not sure what is best way to handle it. Should I include
these patches in the series ? or just mention them in cover letter ? I am looking
for suggestions on how to best communicate it. thanks
[1] https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git/
My staging tree on github contain these precursor patches.
> $ git show 22db3de
> fatal: ambiguous argument '22db3de': unknown revision or path not in the working tree.
>
> Do you have updated version of the series which you can send out?
>
>> @@ -67,6 +74,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 *);
>
> WARNING: missing space after return type
> #502: FILE: drivers/crypto/ccp/sp-dev.h:79:
> + void(*set_psp_master_device)(struct sp_device *);
>
> Don't forget to run all patches through checkpatch. Some of the warnings
> make sense.
>
> Thx.
>
On Wed, Sep 06, 2017 at 03:38:38PM -0500, Brijesh Singh wrote:
> This bit of my struggle -- tip/master is not in sync with cryptodev-2.6 [1].
Aaha.
> In order to expand the CCP driver we need the following commits from the
> cryptodev-2.6
>
> 57de3aefb73f crypto: ccp - remove ccp_present() check from device initialize
> d0ebbc0c407a crypto: ccp - rename ccp driver initialize files as sp device
> f4d18d656f88 crypto: ccp - Abstract interrupt registeration
> 720419f01832 crypto: ccp - Introduce the AMD Secure Processor device
> 970e8303cb8d crypto: ccp - Use devres interface to allocate PCI/iomap and cleanup
>
> I cherry-picked these patches into tip/master before starting the SEV work.
>
> Since these patches were already reviewed and accepted hence I did not include it
> in my RFC series. I am not sure what is best way to handle it. Should I include
> these patches in the series ? or just mention them in cover letter ? I am looking
> for suggestions on how to best communicate it. thanks
Right, so I'm assuming those will go upstream this merge window, no?
Because if so, the rest of your patches will apply, once Linus merges
them. I guess for the purposes of reviewing, I can cherrypick them too.
Thx.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On 09/06/2017 11:41 AM, Borislav Petkov wrote:
> On Tue, Sep 05, 2017 at 04:39:14PM -0500, Brijesh Singh wrote:
>> Not sure if we need to document the complete measurement flow in the
>> driver doc.
>
> No, not the whole thing - only summarized in a couple of sentences with
> the link to the doc.
>
Will do.
>> I was trying to keep everything to 80 column limit but if that is
>> not an issue for documentation then I like your recommendation.
>
> That rule is not a hard one - rather, it is to human discretion what
> is better - readability or fitting on some small screen, no one uses
> anymore.
>
I will follow your recommendation
>> The command does not require explicit parameter to differentiate between
>> live migration vs snapshot. All it needs is a destination platform
>> PDH key. If its live migration case then VM management stack will probably
>> communicate with remote platform and get its PDH keys before calling us.
>> The KVM driver simply acts upon the request from the userspace. SEV firmware
>> spec Appendix A [1] provides complete flow diagram which need to be implemented
>> in userspace. The driver simply act upon when it asked to create SEND_START
>> context.
>
> Ok, so that only creates the context after sending the PDH cert into the
> firmware. So please state that first and then what the command can be
> used for. The way it is written now, it reads like it does the sending
> of the guest.
>
Will clarify it in documentation.
On 09/06/2017 03:46 PM, Borislav Petkov wrote:
> On Wed, Sep 06, 2017 at 03:38:38PM -0500, Brijesh Singh wrote:
>> This bit of my struggle -- tip/master is not in sync with cryptodev-2.6 [1].
>
> Aaha.
>
>> In order to expand the CCP driver we need the following commits from the
>> cryptodev-2.6
>>
>> 57de3aefb73f crypto: ccp - remove ccp_present() check from device initialize
>> d0ebbc0c407a crypto: ccp - rename ccp driver initialize files as sp device
>> f4d18d656f88 crypto: ccp - Abstract interrupt registeration
>> 720419f01832 crypto: ccp - Introduce the AMD Secure Processor device
>> 970e8303cb8d crypto: ccp - Use devres interface to allocate PCI/iomap and cleanup
>>
>> I cherry-picked these patches into tip/master before starting the SEV work.
>>
>> Since these patches were already reviewed and accepted hence I did not include it
>> in my RFC series. I am not sure what is best way to handle it. Should I include
>> these patches in the series ? or just mention them in cover letter ? I am looking
>> for suggestions on how to best communicate it. thanks
>
> Right, so I'm assuming those will go upstream this merge window, no?
They were included in a pull request (for 4.14) from Herbert, dated Monday.
On Wed, Sep 06, 2017 at 04:26:52PM -0500, Gary R Hook wrote:
> They were included in a pull request (for 4.14) from Herbert, dated Monday.
Right. I rebased the SEV pile ontop of latest upstream and now it applies much
better:
checking file drivers/crypto/ccp/Kconfig
Hunk #1 succeeded at 32 (offset 1 line).
checking file drivers/crypto/ccp/Makefile
checking file drivers/crypto/ccp/psp-dev.c
checking file drivers/crypto/ccp/psp-dev.h
checking file drivers/crypto/ccp/sp-dev.c
Hunk #3 succeeded at 225 (offset 1 line).
Hunk #4 FAILED at 243.
1 out of 4 hunks FAILED
checking file drivers/crypto/ccp/sp-dev.h
Hunk #1 succeeded at 42 with fuzz 2 (offset 1 line).
Hunk #2 succeeded at 75 (offset 1 line).
Hunk #3 succeeded at 114 (offset 1 line).
Hunk #4 succeeded at 143 (offset 1 line).
checking file drivers/crypto/ccp/sp-pci.c
The failed hunk is due to #ifdef CONFIG_PM guards but that's trivial to fix.
Thanks.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:39PM -0500, Brijesh Singh wrote:
> Platform Security Processor (PSP) is part of AMD Secure Processor (AMD-SP),
> PSP is a dedicated processor that provides the support for key management
> commands in a Secure Encrypted Virtualiztion (SEV) mode, along with
> software-based Tursted Executation Environment (TEE) to enable the
^^^^^^^^^^^^^^^^^^^
> third-party tursted applications.
^^^^^^^
Spellcheck pls.
> Cc: Herbert Xu <[email protected]>
> Cc: David S. Miller <[email protected]>
> Cc: Gary Hook <[email protected]>
> Cc: [email protected]
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> drivers/crypto/ccp/Kconfig | 9 ++
> drivers/crypto/ccp/Makefile | 1 +
> drivers/crypto/ccp/psp-dev.c | 226 +++++++++++++++++++++++++++++++++++++++++++
> drivers/crypto/ccp/psp-dev.h | 82 ++++++++++++++++
> drivers/crypto/ccp/sp-dev.c | 43 ++++++++
> drivers/crypto/ccp/sp-dev.h | 41 +++++++-
> drivers/crypto/ccp/sp-pci.c | 46 +++++++++
> 7 files changed, 447 insertions(+), 1 deletion(-)
> create mode 100644 drivers/crypto/ccp/psp-dev.c
> create mode 100644 drivers/crypto/ccp/psp-dev.h
>
> diff --git a/drivers/crypto/ccp/Kconfig b/drivers/crypto/ccp/Kconfig
> index 15b63fd..41c0ff5 100644
> --- a/drivers/crypto/ccp/Kconfig
> +++ b/drivers/crypto/ccp/Kconfig
> @@ -31,3 +31,12 @@ 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 device"
> + default y
> + depends on CRYPTO_DEV_CCP_DD
> + help
> + Provide the support for AMD Platform Security Processor (PSP) device
> + which can be used for communicating with Secure Encryption Virtualization
> + (SEV) firmware.
The commit message above reads better to me as the help text than what
you have here.
Also, in order to make it easier for the user, I think we'll need a
CONFIG_AMD_MEM_ENCRYPT_SEV or so and make that depend on CONFIG_KVM_AMD,
this above and all the other pieces that are needed. Just so that when
the user builds such a kernel, all is enabled and not her having to go
look for what else is needed.
And then put the sev code behind that config option. Depending on how
ugly it gets...
> diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile
> index 5f2adc5..8aae4ff 100644
> --- a/drivers/crypto/ccp/Makefile
> +++ b/drivers/crypto/ccp/Makefile
> @@ -7,6 +7,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 \
> diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c
> new file mode 100644
> index 0000000..bb0ea9a
> --- /dev/null
> +++ b/drivers/crypto/ccp/psp-dev.c
> @@ -0,0 +1,226 @@
> +/*
> + * AMD Platform Security Processor (PSP) interface
> + *
> + * Copyright (C) 2016 Advanced Micro Devices, Inc.
> + *
> + * Author: Brijesh Singh <[email protected]>
> + *
> + * 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"
> +
> +static LIST_HEAD(psp_devs);
> +static DEFINE_SPINLOCK(psp_devs_lock);
> +
> +const struct psp_vdata psp_entry = {
> + .offset = 0x10500,
> +};
> +
> +void psp_add_device(struct psp_device *psp)
That function is needlessly global and should be static, AFAICT.
Better yet, it is called only once and its body is trivial so you can
completely get rid of it and meld it into the callsite.
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&psp_devs_lock, flags);
> +
> + list_add_tail(&psp->entry, &psp_devs);
> +
> + spin_unlock_irqrestore(&psp_devs_lock, flags);
> +}
> +
> +void psp_del_device(struct psp_device *psp)
Ditto.
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&psp_devs_lock, flags);
> +
> + list_del(&psp->entry);
> + spin_unlock_irqrestore(&psp_devs_lock, flags);
> +}
> +
> +static struct psp_device *psp_alloc_struct(struct sp_device *sp)
"psp_alloc()" is enough I guess.
> +{
> + 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;
> +}
> +
> +irqreturn_t psp_irq_handler(int irq, void *data)
static.
Please audit all your functions in the psp pile and make them static if
not needed outside of their compilation unit.
> +{
> + unsigned int status;
> + irqreturn_t ret = IRQ_HANDLED;
> + struct psp_device *psp = data;
Please sort function local variables declaration in a reverse christmas
tree order:
<type> longest_variable_name;
<type> shorter_var_name;
<type> even_shorter;
<type> i;
> +
> + /* read the interrupt status */
> + status = ioread32(psp->io_regs + PSP_P2CMSG_INTSTS);
> +
> + /* invoke subdevice interrupt handlers */
> + if (status) {
> + if (psp->sev_irq_handler)
> + ret = psp->sev_irq_handler(irq, psp->sev_irq_data);
> + if (psp->tee_irq_handler)
> + ret = psp->tee_irq_handler(irq, psp->tee_irq_data);
> + }
> +
> + /* clear the interrupt status */
> + iowrite32(status, psp->io_regs + PSP_P2CMSG_INTSTS);
We're clearing the status by writing the same value back?!? Shouldn't
that be:
iowrite32(0, psp->io_regs + PSP_P2CMSG_INTSTS);
Below I see
iowrite32(0xffffffff, psp->io_regs + PSP_P2CMSG_INTSTS);
which is supposed to clear IRQs. Btw, you can write that:
iowrite32(-1, psp->io_regs + PSP_P2CMSG_INTSTS);
> +
> + return ret;
> +}
> +
> +static int psp_init(struct psp_device *psp)
> +{
> + psp_add_device(psp);
> +
> + return 0;
> +}
A function which should be returning void.
> +
> +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;
<---- newline here.
> + 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(0xffffffff, psp->io_regs + PSP_P2CMSG_INTSTS);
> +
> + dev_dbg(dev, "requesting an IRQ ...\n");
> + /* 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;
> + }
> +
> + sp_set_psp_master(sp);
So this function is called only once and declared somewhere else. You
could simply do here:
if (sp->set_psp_master_device)
sp->set_psp_master_device(sp);
and get rid of one more global function.
> +
> + dev_dbg(dev, "initializing psp\n");
> + ret = psp_init(psp);
> + if (ret) {
> + dev_err(dev, "failed to init psp\n");
> + goto e_irq;
> + }
That error handling will never execute. Thus the e_irq label is not
needed too.
> +
> + /* Enable interrupt */
> + dev_dbg(dev, "Enabling interrupts ...\n");
> + iowrite32(7, psp->io_regs + PSP_P2CMSG_INTEN);
Uh, a magic "7"! Exciting!
I wonder what that means and whether it could be a define with an
explanatory name instead. Ditto for the other values...
> +
> + dev_notice(dev, "psp enabled\n");
> +
> + 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;
> +
> + sp_free_psp_irq(sp, psp);
> +
> + psp_del_device(psp);
> +}
> +
> +int psp_dev_resume(struct sp_device *sp)
> +{
> + return 0;
> +}
> +
> +int psp_dev_suspend(struct sp_device *sp, pm_message_t state)
> +{
> + return 0;
> +}
Those last two are completely useless. Delete them pls.
> +
> +int psp_request_tee_irq(struct psp_device *psp, irq_handler_t handler,
> + void *data)
> +{
> + psp->tee_irq_data = data;
> + psp->tee_irq_handler = handler;
> +
> + return 0;
> +}
void
> +
> +int psp_free_tee_irq(struct psp_device *psp, void *data)
> +{
> + if (psp->tee_irq_handler) {
> + psp->tee_irq_data = NULL;
> + psp->tee_irq_handler = NULL;
> + }
> +
> + return 0;
> +}
void
> +
> +int psp_request_sev_irq(struct psp_device *psp, irq_handler_t handler,
> + void *data)
> +{
> + psp->sev_irq_data = data;
> + psp->sev_irq_handler = handler;
> +
> + return 0;
> +}
> +
> +int psp_free_sev_irq(struct psp_device *psp, void *data)
> +{
> + if (psp->sev_irq_handler) {
> + psp->sev_irq_data = NULL;
> + psp->sev_irq_handler = NULL;
> + }
> +
> + return 0;
> +}
Both void. Please do not return values from functions which are simply
void functions by design.
> +
> +struct psp_device *psp_get_master_device(void)
> +{
> + struct sp_device *sp = sp_get_psp_master_device();
> +
> + return sp ? sp->psp_data : NULL;
> +}
> diff --git a/drivers/crypto/ccp/psp-dev.h b/drivers/crypto/ccp/psp-dev.h
> new file mode 100644
> index 0000000..6e167b8
> --- /dev/null
> +++ b/drivers/crypto/ccp/psp-dev.h
> @@ -0,0 +1,82 @@
> +/*
> + * AMD Platform Security Processor (PSP) interface driver
> + *
> + * Copyright (C) 2017 Advanced Micro Devices, Inc.
> + *
> + * Author: Brijesh Singh <[email protected]>
> + *
> + * 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 "sp-dev.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_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 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;
> +
> + irq_handler_t sev_irq_handler;
> + void *sev_irq_data;
> + irq_handler_t tee_irq_handler;
> + void *tee_irq_data;
> +
> + void *sev_data;
> + void *tee_data;
> +};
> +
> +void psp_add_device(struct psp_device *psp);
> +void psp_del_device(struct psp_device *psp);
> +
> +int psp_request_sev_irq(struct psp_device *psp, irq_handler_t handler,
> + void *data);
> +int psp_free_sev_irq(struct psp_device *psp, void *data);
> +
> +int psp_request_tee_irq(struct psp_device *psp, irq_handler_t handler,
> + void *data);
Let them stick out.
> +int psp_free_tee_irq(struct psp_device *psp, void *data);
> +
> +struct psp_device *psp_get_master_device(void);
> +
> +extern const struct psp_vdata psp_entry;
> +
> +#endif /* __PSP_DEV_H */
> diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c
So this file is called sp-dev and the other psp-dev. Confusing.
And in general, why isn't the whole thing a single psp-dev and you can
save yourself all the registering blabla and have a single driver for
the whole PSP functionality?
Distros will have to enable everything anyway and the whole CCP/PSP code
is only a couple of KBs so you can just as well put it all into a single
driver. Hm.
> @@ -102,6 +113,8 @@ 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);
> +void sp_set_psp_master(struct sp_device *sp);
> +struct sp_device *sp_get_psp_master_device(void);
>
> #ifdef CONFIG_CRYPTO_DEV_SP_CCP
>
> @@ -129,4 +142,30 @@ 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_dev_destroy(struct sp_device *sp);
> +
> +int psp_dev_suspend(struct sp_device *sp, pm_message_t state);
> +int psp_dev_resume(struct sp_device *sp);
> +#else /* !CONFIG_CRYPTO_DEV_SP_PSP */
> +
> +static inline int psp_dev_init(struct sp_device *sp)
> +{
> + return 0;
> +}
> +static inline void psp_dev_destroy(struct sp_device *sp) { }
> +
> +static inline int psp_dev_suspend(struct sp_device *sp, pm_message_t state)
> +{
> + return 0;
> +}
> +static inline int psp_dev_resume(struct sp_device *sp)
> +{
> + return 0;
> +}
Put them on a single line:
static inline int psp_dev_resume(struct sp_device *sp) { return 0; }
and so on... Do that for the rest of the function stubs in the headers pls.
Thx.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
Hi Boris,
On 09/07/2017 09:27 AM, Borislav Petkov wrote:
...
>
> The commit message above reads better to me as the help text than what
> you have here.
>
> Also, in order to make it easier for the user, I think we'll need a
> CONFIG_AMD_MEM_ENCRYPT_SEV or so and make that depend on CONFIG_KVM_AMD,
> this above and all the other pieces that are needed. Just so that when
> the user builds such a kernel, all is enabled and not her having to go
> look for what else is needed.
>
> And then put the sev code behind that config option. Depending on how
> ugly it gets...
>
I will add more detail in the help text. I will look into adding some
depends.
...
>> +
>> +void psp_add_device(struct psp_device *psp)
>
> That function is needlessly global and should be static, AFAICT.
>
> Better yet, it is called only once and its body is trivial so you can
> completely get rid of it and meld it into the callsite.
>
Agreed, will do.
.....
>> +
>> +static struct psp_device *psp_alloc_struct(struct sp_device *sp)
>
> "psp_alloc()" is enough I guess.
>
I was trying to adhere to the existing ccp-dev.c function naming
conversion.
....
>
> static.
>
> Please audit all your functions in the psp pile and make them static if
> not needed outside of their compilation unit.
>
Will do.
>> +{
>> + unsigned int status;
>> + irqreturn_t ret = IRQ_HANDLED;
>> + struct psp_device *psp = data;
>
> Please sort function local variables declaration in a reverse christmas
> tree order:
>
> <type> longest_variable_name;
> <type> shorter_var_name;
> <type> even_shorter;
> <type> i;
>
Got it, will do
>> +
>> + /* read the interrupt status */
>> + status = ioread32(psp->io_regs + PSP_P2CMSG_INTSTS);
>> +
>> + /* invoke subdevice interrupt handlers */
>> + if (status) {
>> + if (psp->sev_irq_handler)
>> + ret = psp->sev_irq_handler(irq, psp->sev_irq_data);
>> + if (psp->tee_irq_handler)
>> + ret = psp->tee_irq_handler(irq, psp->tee_irq_data);
>> + }
>> +
>> + /* clear the interrupt status */
>> + iowrite32(status, psp->io_regs + PSP_P2CMSG_INTSTS);
>
> We're clearing the status by writing the same value back?!? Shouldn't
> that be:
>
> iowrite32(0, psp->io_regs + PSP_P2CMSG_INTSTS);
>
Actually the SW should write "1" to clear the bit. To make it clear, I
can use value 1 and add comment.
> Below I see
>
> iowrite32(0xffffffff, psp->io_regs + PSP_P2CMSG_INTSTS);
>
> which is supposed to clear IRQs. Btw, you can write that:
>
> iowrite32(-1, psp->io_regs + PSP_P2CMSG_INTSTS);
>
Sure, I will do that
...
...
>> +
>> + sp_set_psp_master(sp);
>
> So this function is called only once and declared somewhere else. You
> could simply do here:
>
> if (sp->set_psp_master_device)
> sp->set_psp_master_device(sp);
>
> and get rid of one more global function.
Sure I can do that.
....
>> + /* Enable interrupt */
>> + dev_dbg(dev, "Enabling interrupts ...\n");
>> + iowrite32(7, psp->io_regs + PSP_P2CMSG_INTEN);
>
> Uh, a magic "7"! Exciting!
>
> I wonder what that means and whether it could be a define with an
> explanatory name instead. Ditto for the other values...
>
I will try to define some macro instead of hard coded values.
....
>> +
>> +int psp_dev_resume(struct sp_device *sp)
>> +{
>> + return 0;
>> +}
>> +
>> +int psp_dev_suspend(struct sp_device *sp, pm_message_t state)
>> +{
>> + return 0;
>> +}
>
> Those last two are completely useless. Delete them pls.
>
We don't have any PM support, I agree will delete it.
...
>> +int psp_request_sev_irq(struct psp_device *psp, irq_handler_t handler,
>> + void *data)
>> +{
>> + psp->sev_irq_data = data;
>> + psp->sev_irq_handler = handler;
>> +
>> + return 0;
>> +}
>> +
>> +int psp_free_sev_irq(struct psp_device *psp, void *data)
>> +{
>> + if (psp->sev_irq_handler) {
>> + psp->sev_irq_data = NULL;
>> + psp->sev_irq_handler = NULL;
>> + }
>> +
>> + return 0;
>> +}
>
> Both void. Please do not return values from functions which are simply
> void functions by design.
>
thanks, will fix it.
...
>> +int psp_request_sev_irq(struct psp_device *psp, irq_handler_t handler,
>> + void *data);
>> +int psp_free_sev_irq(struct psp_device *psp, void *data);
>> +
>> +int psp_request_tee_irq(struct psp_device *psp, irq_handler_t handler,
>> + void *data);
>
> Let them stick out.
okay
...
>
>> +int psp_free_tee_irq(struct psp_device *psp, void *data);
>> +
>> +struct psp_device *psp_get_master_device(void);
>> +
>> +extern const struct psp_vdata psp_entry;
>> +
>> +#endif /* __PSP_DEV_H */
>> diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c
>
> So this file is called sp-dev and the other psp-dev. Confusing.
>
> And in general, why isn't the whole thing a single psp-dev and you can
> save yourself all the registering blabla and have a single driver for
> the whole PSP functionality?
>
> Distros will have to enable everything anyway and the whole CCP/PSP code
> is only a couple of KBs so you can just as well put it all into a single
> driver. Hm.
>
PSP provides the interface for communicating with SEV and TEE FWs. I choose
to add generic PSP interface first then plug the SEV FW support. The TEE
commands may be totally different from SEV FW commands hence I tried to put
all the SEV specific changes into one place and adhere to current ccp file
naming convention.
At high level, AMD-SP (AMD Secure Processor) (i.e CCP driver) will provide the
support for CCP, SEV and TEE FW commands.
+--- CCP
|
AMD-SP --|
| +--- SEV
| |
+---- PSP ---*
|
+---- TEE
-Brijesh
On 09/07/2017 05:19 PM, Brijesh Singh wrote:
> Hi Boris,
>
> On 09/07/2017 09:27 AM, Borislav Petkov wrote:
>
> ...
>
>>
>> The commit message above reads better to me as the help text than what
>> you have here.
>>
>> Also, in order to make it easier for the user, I think we'll need a
>> CONFIG_AMD_MEM_ENCRYPT_SEV or so and make that depend on CONFIG_KVM_AMD,
>> this above and all the other pieces that are needed. Just so that when
>> the user builds such a kernel, all is enabled and not her having to go
>> look for what else is needed.
>>
>> And then put the sev code behind that config option. Depending on how
>> ugly it gets...
>>
>
> I will add more detail in the help text. I will look into adding some
> depends.
>
> ...
>
>>> +
>>> +void psp_add_device(struct psp_device *psp)
>>
>> That function is needlessly global and should be static, AFAICT.
>>
>> Better yet, it is called only once and its body is trivial so you can
>> completely get rid of it and meld it into the callsite.
>>
>
> Agreed, will do.
>
> .....
>
>>> +
>>> +static struct psp_device *psp_alloc_struct(struct sp_device *sp)
>>
>> "psp_alloc()" is enough I guess.
>>
>
> I was trying to adhere to the existing ccp-dev.c function naming
> conversion.
I would prefer that we not shorten this. The prior incarnation,
ccp_alloc_struct(), has/had been around for a while. And there are a
number of
similarly named allocation functions in the driver that we like to keep
sorted.
If anything, it should be more explanatory, IMO.
>
> ....
>
>>
>> static.
>>
>> Please audit all your functions in the psp pile and make them static if
>> not needed outside of their compilation unit.
>>
>
> Will do.
>
>>> +{
>>> + unsigned int status;
>>> + irqreturn_t ret = IRQ_HANDLED;
>>> + struct psp_device *psp = data;
>>
>> Please sort function local variables declaration in a reverse christmas
>> tree order:
>>
>> <type> longest_variable_name;
>> <type> shorter_var_name;
>> <type> even_shorter;
>> <type> i;
>>
>
> Got it, will do
>
>
>>> +
>>> + /* read the interrupt status */
>>> + status = ioread32(psp->io_regs + PSP_P2CMSG_INTSTS);
>>> +
>>> + /* invoke subdevice interrupt handlers */
>>> + if (status) {
>>> + if (psp->sev_irq_handler)
>>> + ret = psp->sev_irq_handler(irq, psp->sev_irq_data);
>>> + if (psp->tee_irq_handler)
>>> + ret = psp->tee_irq_handler(irq, psp->tee_irq_data);
>>> + }
>>> +
>>> + /* clear the interrupt status */
>>> + iowrite32(status, psp->io_regs + PSP_P2CMSG_INTSTS);
>>
>> We're clearing the status by writing the same value back?!? Shouldn't
>> that be:
>>
>> iowrite32(0, psp->io_regs + PSP_P2CMSG_INTSTS);
>>
>
> Actually the SW should write "1" to clear the bit. To make it clear, I
> can use value 1 and add comment.
>
>
>
>> Below I see
>>
>> iowrite32(0xffffffff, psp->io_regs + PSP_P2CMSG_INTSTS);
>>
>> which is supposed to clear IRQs. Btw, you can write that:
>>
>> iowrite32(-1, psp->io_regs + PSP_P2CMSG_INTSTS);
>>
>
> Sure, I will do that
>
> ...
>
> ...
>
>>> +
>>> + sp_set_psp_master(sp);
>>
>> So this function is called only once and declared somewhere else. You
>> could simply do here:
>>
>> if (sp->set_psp_master_device)
>> sp->set_psp_master_device(sp);
>>
>> and get rid of one more global function.
>
>
> Sure I can do that.
>
> ....
>
>>> + /* Enable interrupt */
>>> + dev_dbg(dev, "Enabling interrupts ...\n");
>>> + iowrite32(7, psp->io_regs + PSP_P2CMSG_INTEN);
>>
>> Uh, a magic "7"! Exciting!
>>
>> I wonder what that means and whether it could be a define with an
>> explanatory name instead. Ditto for the other values...
>>
>
>
> I will try to define some macro instead of hard coded values.
>
> ....
>
>>> +
>>> +int psp_dev_resume(struct sp_device *sp)
>>> +{
>>> + return 0;
>>> +}
>>> +
>>> +int psp_dev_suspend(struct sp_device *sp, pm_message_t state)
>>> +{
>>> + return 0;
>>> +}
>>
>> Those last two are completely useless. Delete them pls.
>>
>
> We don't have any PM support, I agree will delete it.
>
> ...
>
>>> +int psp_request_sev_irq(struct psp_device *psp, irq_handler_t handler,
>>> + void *data)
>>> +{
>>> + psp->sev_irq_data = data;
>>> + psp->sev_irq_handler = handler;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +int psp_free_sev_irq(struct psp_device *psp, void *data)
>>> +{
>>> + if (psp->sev_irq_handler) {
>>> + psp->sev_irq_data = NULL;
>>> + psp->sev_irq_handler = NULL;
>>> + }
>>> +
>>> + return 0;
>>> +}
>>
>> Both void. Please do not return values from functions which are simply
>> void functions by design.
>>
>
> thanks, will fix it.
>
> ...
>
>>> +int psp_request_sev_irq(struct psp_device *psp, irq_handler_t handler,
>>> + void *data);
>>> +int psp_free_sev_irq(struct psp_device *psp, void *data);
>>> +
>>> +int psp_request_tee_irq(struct psp_device *psp, irq_handler_t handler,
>>> + void *data);
>>
>> Let them stick out.
>
> okay
>
> ...
>
>>
>>> +int psp_free_tee_irq(struct psp_device *psp, void *data);
>>> +
>>> +struct psp_device *psp_get_master_device(void);
>>> +
>>> +extern const struct psp_vdata psp_entry;
>>> +
>>> +#endif /* __PSP_DEV_H */
>>> diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c
>>
>> So this file is called sp-dev and the other psp-dev. Confusing.
>>
>> And in general, why isn't the whole thing a single psp-dev and you can
>> save yourself all the registering blabla and have a single driver for
>> the whole PSP functionality?
>>
>> Distros will have to enable everything anyway and the whole CCP/PSP code
>> is only a couple of KBs so you can just as well put it all into a single
>> driver. Hm.
>>
>
> PSP provides the interface for communicating with SEV and TEE FWs. I choose
> to add generic PSP interface first then plug the SEV FW support. The TEE
> commands may be totally different from SEV FW commands hence I tried to put
> all the SEV specific changes into one place and adhere to current ccp file
> naming convention.
>
> At high level, AMD-SP (AMD Secure Processor) (i.e CCP driver) will
> provide the
> support for CCP, SEV and TEE FW commands.
>
>
> +--- CCP
> |
> AMD-SP --|
> | +--- SEV
> | |
> +---- PSP ---*
> |
> +---- TEE
>
> -Brijesh
On Thu, Sep 07, 2017 at 06:15:55PM -0500, Gary R Hook wrote:
> I would prefer that we not shorten this. The prior incarnation,
> ccp_alloc_struct(), has/had been around for a while. And there are a
> number of similarly named allocation functions in the driver that we
> like to keep sorted. If anything, it should be more explanatory, IMO.
Well, looks like an AMD practice:
$ git grep --name-only alloc_struct
drivers/crypto/ccp/ccp-dev.c
drivers/crypto/ccp/ccp-dev.h
drivers/crypto/ccp/psp-dev.c
drivers/crypto/ccp/sev-dev.c
drivers/crypto/ccp/sp-dev.c
drivers/crypto/ccp/sp-dev.h
drivers/crypto/ccp/sp-pci.c
drivers/crypto/ccp/sp-platform.c
drivers/gpu/drm/amd/amdkfd/kfd_dbgmgr.c
drivers/gpu/drm/amd/amdkfd/kfd_priv.h
drivers/gpu/drm/amd/amdkfd/kfd_topology.c
But whatever, if you like it more that way... :)
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Thu, Sep 07, 2017 at 05:19:32PM -0500, Brijesh Singh wrote:
> At high level, AMD-SP (AMD Secure Processor) (i.e CCP driver) will provide the
> support for CCP, SEV and TEE FW commands.
>
>
> +--- CCP
> |
> AMD-SP --|
> | +--- SEV
> | |
> +---- PSP ---*
> |
> +---- TEE
I still don't see the need for such finegrained separation, though.
There's no "this is a separate compilation unit because... ". At least
the PSP branch could be a single driver without the interface.
For example, psp_request_sev_irq() is called only by sev_dev_init(). So
why is sev-dev a separate compilation unit? Is anything else going to
use the PSP interface?
If not, just put it all in a psp-dev file and that's it. We have a
gazillion config options and having two more just because, is not a good
reason. You can always carve it out later if there's real need. But if
the SEV thing can't function without the PSP thing, then you can just as
well put it inside it.
This way you can save yourself a bunch of exported functions and the
like.
Another example for not optimal design is psp_request_tee_irq() - it
doesn't really request an irq by calling into the IRQ core but simply
assigns a handler. Which looks to me like you're simulating an interface
where one is not really needed. Ditto for the sev_irq version, btw.
And where are the psp_request_tee_irq() et al callers? Nothing calls
those functions. So you can save yourself all that needless glue if you
put them in a single psp-dev and have that functionality always present
when you enable the PSP.
Because this is what the PSP does - SEV and TEE services. Why would you
have CRYPTO_DEV_PSP_SEV depend on CRYPTO_DEV_SP_PSP where the SEV and
TEE functionality are integral part of it?
And so on and so on...
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On 09/08/2017 03:40 AM, Borislav Petkov wrote:
> On Thu, Sep 07, 2017 at 05:19:32PM -0500, Brijesh Singh wrote:
>> At high level, AMD-SP (AMD Secure Processor) (i.e CCP driver) will provide the
>> support for CCP, SEV and TEE FW commands.
>>
>>
>> +--- CCP
>> |
>> AMD-SP --|
>> | +--- SEV
>> | |
>> +---- PSP ---*
>> |
>> +---- TEE
>
> I still don't see the need for such finegrained separation, though.
> There's no "this is a separate compilation unit because... ". At least
> the PSP branch could be a single driver without the interface.
>
> For example, psp_request_sev_irq() is called only by sev_dev_init(). So
> why is sev-dev a separate compilation unit? Is anything else going to
> use the PSP interface?
>
I don't know anything about the TEE support hence I don't have very strong
reason for finegrained separation -- I just wanted to ensure that the SEV
enablement does not interfere with TEE support in the future.
> If not, just put it all in a psp-dev file and that's it. We have a
> gazillion config options and having two more just because, is not a good
> reason. You can always carve it out later if there's real need. But if
> the SEV thing can't function without the PSP thing, then you can just as
> well put it inside it.
>
> This way you can save yourself a bunch of exported functions and the
> like.
>
> Another example for not optimal design is psp_request_tee_irq() - it
> doesn't really request an irq by calling into the IRQ core but simply
> assigns a handler. Which looks to me like you're simulating an interface
> where one is not really needed. Ditto for the sev_irq version, btw.
>
It's possible that both TEE and SEV share the same interrupt but their
interrupt handling approach could be totally different hence I tried to
abstract it.
I am making several assumption on TEE side without knowing in detail ;)
I can go with your recommendation -- we can always crave it out later once
the TEE support is visible.
-Brijesh
On 09/08/2017 03:40 AM, Borislav Petkov wrote:
> On Thu, Sep 07, 2017 at 05:19:32PM -0500, Brijesh Singh wrote:
>> At high level, AMD-SP (AMD Secure Processor) (i.e CCP driver) will provide the
>> support for CCP, SEV and TEE FW commands.
>>
>>
>> +--- CCP
>> |
>> AMD-SP --|
>> | +--- SEV
>> | |
>> +---- PSP ---*
>> |
>> +---- TEE
>
> I still don't see the need for such finegrained separation, though.
> There's no "this is a separate compilation unit because... ". At least
> the PSP branch could be a single driver without the interface.
>
> For example, psp_request_sev_irq() is called only by sev_dev_init(). So
> why is sev-dev a separate compilation unit? Is anything else going to
> use the PSP interface?
I don't know anything about the TEE support hence I don't have very strong
reason for finegrained separation -- I just wanted to ensure that the SEV
enablement does not interfere with TEE support in the future.
>
> If not, just put it all in a psp-dev file and that's it. We have a
> gazillion config options and having two more just because, is not a good
> reason. You can always carve it out later if there's real need. But if
> the SEV thing can't function without the PSP thing, then you can just as
> well put it inside it.
>
> This way you can save yourself a bunch of exported functions and the
> like.
>
> Another example for not optimal design is psp_request_tee_irq() - it
> doesn't really request an irq by calling into the IRQ core but simply
> assigns a handler. Which looks to me like you're simulating an interface
> where one is not really needed. Ditto for the sev_irq version, btw.
>
It's possible that both TEE and SEV share the same interrupt but their
interrupt handling approach could be totally different hence I tried to
abstract it.
I am making several assumption on TEE side without knowing in detail
I can go with your recommendation -- we can always crave it out later once
the TEE support is visible.
-Brijesh
Just a cursory review: more indepth after the redesign.
On Mon, Jul 24, 2017 at 03:02:40PM -0500, Brijesh Singh wrote:
> AMDs new Secure Encrypted Virtualization (SEV) feature allows the memory
> contents of a virtual machine to be transparently encrypted with a key
> unique to the guest VM. The programming and management of the encryption
> keys are handled by the AMD Secure Processor (AMD-SP), which exposes the
> commands for these tasks. The complete spec for various commands are
s/ for various commands are/is/
> available at:
> http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
>
> This patch extends AMD-SP driver to provide:
Never say "This patch" in a commit message of a patch. It is
tautologically useless.
> - a in-kernel APIs to communicate with SEV device. The APIs can be used
s/a/an/ with a SEV ...
> by the hypervisor to create encryption context for the SEV guests.
>
> - a userspace IOCTL to manage the platform certificates etc
>
> Cc: Herbert Xu <[email protected]>
> Cc: David S. Miller <[email protected]>
> Cc: Gary Hook <[email protected]>
> Cc: [email protected]
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> drivers/crypto/ccp/Kconfig | 10 +
> drivers/crypto/ccp/Makefile | 1 +
> drivers/crypto/ccp/psp-dev.c | 4 +
> drivers/crypto/ccp/psp-dev.h | 27 ++
> drivers/crypto/ccp/sev-dev.c | 416 ++++++++++++++++++++++++++
> drivers/crypto/ccp/sev-dev.h | 67 +++++
> drivers/crypto/ccp/sev-ops.c | 457 +++++++++++++++++++++++++++++
> drivers/crypto/ccp/sp-pci.c | 2 +-
> include/linux/psp-sev.h | 683 +++++++++++++++++++++++++++++++++++++++++++
> include/uapi/linux/psp-sev.h | 110 +++++++
> 10 files changed, 1776 insertions(+), 1 deletion(-)
> create mode 100644 drivers/crypto/ccp/sev-dev.c
> create mode 100644 drivers/crypto/ccp/sev-dev.h
> create mode 100644 drivers/crypto/ccp/sev-ops.c
> create mode 100644 include/linux/psp-sev.h
> create mode 100644 include/uapi/linux/psp-sev.h
>
> diff --git a/drivers/crypto/ccp/Kconfig b/drivers/crypto/ccp/Kconfig
> index 41c0ff5..ae0ff1c 100644
> --- a/drivers/crypto/ccp/Kconfig
> +++ b/drivers/crypto/ccp/Kconfig
> @@ -40,3 +40,13 @@ config CRYPTO_DEV_SP_PSP
> Provide the support for AMD Platform Security Processor (PSP) device
> which can be used for communicating with Secure Encryption Virtualization
> (SEV) firmware.
> +
> +config CRYPTO_DEV_PSP_SEV
> + bool "Secure Encrypted Virtualization (SEV) interface"
> + default y
> + depends on CRYPTO_DEV_CCP_DD
> + depends on CRYPTO_DEV_SP_PSP
> + help
> + Provide the kernel and userspace (/dev/sev) interface to communicate with
> + Secure Encrypted Virtualization (SEV) firmware running inside AMD Platform
> + Security Processor (PSP)
> diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile
> index 8aae4ff..94ca748 100644
> --- a/drivers/crypto/ccp/Makefile
> +++ b/drivers/crypto/ccp/Makefile
> @@ -8,6 +8,7 @@ ccp-$(CONFIG_CRYPTO_DEV_SP_CCP) += ccp-dev.o \
> ccp-debugfs.o
> ccp-$(CONFIG_PCI) += sp-pci.o
> ccp-$(CONFIG_CRYPTO_DEV_SP_PSP) += psp-dev.o
> +ccp-$(CONFIG_CRYPTO_DEV_PSP_SEV) += sev-dev.o sev-ops.o
>
> obj-$(CONFIG_CRYPTO_DEV_CCP_CRYPTO) += ccp-crypto.o
> ccp-crypto-objs := ccp-crypto-main.o \
> diff --git a/drivers/crypto/ccp/psp-dev.c b/drivers/crypto/ccp/psp-dev.c
> index bb0ea9a..0c9d25c 100644
> --- a/drivers/crypto/ccp/psp-dev.c
> +++ b/drivers/crypto/ccp/psp-dev.c
> @@ -97,6 +97,7 @@ irqreturn_t psp_irq_handler(int irq, void *data)
> static int psp_init(struct psp_device *psp)
> {
> psp_add_device(psp);
> + sev_dev_init(psp);
>
> return 0;
> }
> @@ -166,17 +167,20 @@ void psp_dev_destroy(struct sp_device *sp)
> struct psp_device *psp = sp->psp_data;
>
> sp_free_psp_irq(sp, psp);
> + sev_dev_destroy(psp);
>
> psp_del_device(psp);
> }
>
> int psp_dev_resume(struct sp_device *sp)
> {
> + sev_dev_resume(sp->psp_data);
> return 0;
> }
>
> int psp_dev_suspend(struct sp_device *sp, pm_message_t state)
> {
> + sev_dev_suspend(sp->psp_data, state);
> return 0;
> }
>
> diff --git a/drivers/crypto/ccp/psp-dev.h b/drivers/crypto/ccp/psp-dev.h
> index 6e167b8..9334d87 100644
> --- a/drivers/crypto/ccp/psp-dev.h
> +++ b/drivers/crypto/ccp/psp-dev.h
> @@ -78,5 +78,32 @@ int psp_free_tee_irq(struct psp_device *psp, void *data);
> struct psp_device *psp_get_master_device(void);
>
> extern const struct psp_vdata psp_entry;
> +#ifdef CONFIG_CRYPTO_DEV_PSP_SEV
> +
> +int sev_dev_init(struct psp_device *psp);
> +void sev_dev_destroy(struct psp_device *psp);
> +int sev_dev_resume(struct psp_device *psp);
> +int sev_dev_suspend(struct psp_device *psp, pm_message_t state);
> +
> +#else /* !CONFIG_CRYPTO_DEV_PSP_SEV */
> +
> +static inline int sev_dev_init(struct psp_device *psp)
> +{
> + return -ENODEV;
> +}
> +
> +static inline void sev_dev_destroy(struct psp_device *psp) { }
> +
> +static inline int sev_dev_resume(struct psp_device *psp)
> +{
> + return -ENODEV;
> +}
> +
> +static inline int sev_dev_suspend(struct psp_device *psp, pm_message_t state)
> +{
> + return -ENODEV;
> +}
> +
> +#endif /* CONFIG_CRYPTO_DEV_PSP_SEV */
>
> #endif /* __PSP_DEV_H */
> diff --git a/drivers/crypto/ccp/sev-dev.c b/drivers/crypto/ccp/sev-dev.c
> new file mode 100644
> index 0000000..a2b41dd
> --- /dev/null
> +++ b/drivers/crypto/ccp/sev-dev.c
> @@ -0,0 +1,416 @@
> +/*
> + * AMD Secure Encrypted Virtualization (SEV) interface
> + *
> + * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
> + *
> + * Author: Brijesh Singh <[email protected]>
> + *
> + * 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/wait.h>
> +#include <linux/jiffies.h>
> +
> +#include "psp-dev.h"
> +#include "sev-dev.h"
> +
> +extern const struct file_operations sev_fops;
> +
> +static LIST_HEAD(sev_devs);
> +static DEFINE_SPINLOCK(sev_devs_lock);
> +static atomic_t sev_id;
> +
> +static unsigned int sev_poll;
> +module_param(sev_poll, uint, 0444);
> +MODULE_PARM_DESC(sev_poll, "Poll for sev command completion - any non-zero value");
> +
> +DEFINE_MUTEX(sev_cmd_mutex);
Please audit all those drivers after you redesign them and check for
variables and functions which are not static but used in a single
compilation unit. Like this one, for example.
<... skipping over the rest, will look at it in the next version ...>
...
> +static int sev_cmd_buffer_len(int cmd)
> +{
> + int size;
> +
> + switch (cmd) {
> + case SEV_CMD_INIT:
> + size = sizeof(struct sev_data_init);
> + break;
You could make that more tabular like this:
case SEV_CMD_INIT: return sizeof(struct sev_data_init);
case SEV_CMD_PLATFORM_STATUS: return sizeof(struct sev_data_status);
case SEV_CMD_PEK_CSR: return sizeof(struct sev_data_pek_csr);
...
which should make it more readable.
But looking at this more, this is a static mapping between the commands
and the corresponding struct sizes and you use it in
print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data,
sev_cmd_buffer_len(cmd), false);
But then, I don't see what that brings you because you're not dumping
the actual @data length but the *expected* data length based on the
command type.
And *that* you can look up in the manual and do not need it in code,
enlarging the driver unnecessarily.
...
> +int sev_platform_init(struct sev_data_init *data, int *error)
> +{
> + return sev_issue_cmd(SEV_CMD_INIT, data, error);
> +}
> +EXPORT_SYMBOL_GPL(sev_platform_init);
> +
> +int sev_platform_shutdown(int *error)
> +{
> + return sev_issue_cmd(SEV_CMD_SHUTDOWN, 0, error);
> +}
> +EXPORT_SYMBOL_GPL(sev_platform_shutdown);
All those could be static inlines in a header instead of separate
symbols.
> +int sev_issue_cmd(int cmd, void *data, int *psp_ret)
> +{
> + struct sev_device *sev = get_sev_master_device();
> + unsigned int phys_lsb, phys_msb;
> + unsigned int reg, ret;
> +
> + if (!sev)
> + return -ENODEV;
> +
> + if (psp_ret)
> + *psp_ret = 0;
> +
> + /* 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;
> +
> + dev_dbg(sev->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);
> +
> + /* Only one command at a time... */
> + mutex_lock(&sev_cmd_mutex);
> +
> + iowrite32(phys_lsb, sev->io_regs + PSP_CMDBUFF_ADDR_LO);
> + iowrite32(phys_msb, sev->io_regs + PSP_CMDBUFF_ADDR_HI);
> + wmb();
WARNING: memory barrier without comment
#503: FILE: drivers/crypto/ccp/sev-dev.c:339:
+ wmb();
Again:
Please integrate scripts/checkpatch.pl into your patch creation
workflow. Some of the warnings/errors *actually* make sense.
> diff --git a/drivers/crypto/ccp/sev-dev.h b/drivers/crypto/ccp/sev-dev.h
> new file mode 100644
> index 0000000..0a8ce08
> --- /dev/null
> +++ b/drivers/crypto/ccp/sev-dev.h
> @@ -0,0 +1,67 @@
> +/*
> + * AMD Secure Encrypted Virtualization (SEV) interface
> + *
> + * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
> + *
> + * Author: Brijesh Singh <[email protected]>
> + *
> + * 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 __SEV_DEV_H__
> +#define __SEV_DEV_H__
> +
> +#include <linux/device.h>
> +#include <linux/spinlock.h>
> +#include <linux/mutex.h>
> +#include <linux/list.h>
> +#include <linux/wait.h>
> +#include <linux/interrupt.h>
> +#include <linux/irqreturn.h>
> +#include <linux/miscdevice.h>
> +
> +#include <linux/psp-sev.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 MAX_PSP_NAME_LEN 16
> +#define SEV_DEFAULT_TIMEOUT 5
> +
> +struct sev_device {
> + struct list_head entry;
> +
> + struct dentry *debugfs;
> + struct miscdevice misc;
> +
> + unsigned int id;
> + char name[MAX_PSP_NAME_LEN];
> +
> + struct device *dev;
> + struct sp_device *sp;
> + struct psp_device *psp;
> +
> + void __iomem *io_regs;
> +
> + unsigned int int_rcvd;
> + wait_queue_head_t int_queue;
> +};
> +
> +void sev_add_device(struct sev_device *sev);
> +void sev_del_device(struct sev_device *sev);
> +
> +int sev_ops_init(struct sev_device *sev);
> +void sev_ops_destroy(struct sev_device *sev);
> +
> +int sev_issue_cmd(int cmd, void *data, int *error);
> +
> +#endif /* __SEV_DEV_H */
> diff --git a/drivers/crypto/ccp/sev-ops.c b/drivers/crypto/ccp/sev-ops.c
> new file mode 100644
> index 0000000..a13d857
> --- /dev/null
> +++ b/drivers/crypto/ccp/sev-ops.c
> @@ -0,0 +1,457 @@
> +/*
> + * AMD Secure Encrypted Virtualization (SEV) command interface
> + *
> + * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
> + *
> + * Author: Brijesh Singh <[email protected]>
> + *
> + * 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/uaccess.h>
> +
> +#include <uapi/linux/psp-sev.h>
> +
> +#include "psp-dev.h"
> +#include "sev-dev.h"
> +
> +static bool sev_initialized;
Variables like this one are a good example that the design is not
optimal. sev-ops should all be in psp-dev and then you don't need all those
registrations and ops passing around and so on... But you get the idea...
> +static int sev_platform_get_state(int *state, int *error)
> +{
> + int ret;
> + struct sev_data_status *data;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + ret = sev_platform_status(data, error);
> + *state = data->state;
> +
> + kfree(data);
> + return ret;
> +}
> +
> +static int __sev_platform_init(int *error)
> +{
> + int ret;
> + struct sev_data_init *data;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + ret = sev_platform_init(data, error);
It is usually the other way around: the function without the "__" calls
the "__" variant.
> +
> + kfree(data);
> + return ret;
> +}
> +
> +static int sev_ioctl_factory_reset(struct sev_issue_cmd *argp)
> +{
> + return sev_issue_cmd(SEV_CMD_FACTORY_RESET, 0, &argp->error);
> +}
static inline in a header.
> +
> +static int sev_ioctl_platform_status(struct sev_issue_cmd *argp)
> +{
> + int ret;
> + struct sev_data_status *data;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + ret = sev_platform_status(data, &argp->error);
> +
> + if (copy_to_user((void *)argp->data, data, sizeof(*data)))
> + ret = -EFAULT;
> +
> + kfree(data);
> + return ret;
> +}
> +
> +static int sev_ioctl_pek_csr(struct sev_issue_cmd *argp)
> +{
> + int do_shutdown = 0;
> + int ret, state, error;
> + void *csr_addr = NULL;
> + struct sev_data_pek_csr *data;
> + struct sev_user_data_pek_csr input;
> +
> + if (copy_from_user(&input, (void *)argp->data,
> + sizeof(struct sev_user_data_pek_csr)))
> + return -EFAULT;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + /*
> + * PEK_CSR command can be issued when firmware is in INIT or WORKING
> + * state. If firmware is in UNINIT state then we transition into INIT
> + * state and issue the command.
> + */
> + ret = sev_platform_get_state(&state, &argp->error);
> + if (ret)
> + return ret;
> +
> + if (state == SEV_STATE_UNINIT) {
> + /* transition the plaform into INIT state */
> + ret = __sev_platform_init(&argp->error);
> + if (ret)
> + return ret;
> + do_shutdown = 1;
> + }
> +
> + if (input.address) {
> + csr_addr = kmalloc(input.length, GFP_KERNEL);
> + if (!csr_addr) {
> + ret = -ENOMEM;
> + goto e_free;
> + }
> + data->address = __psp_pa(csr_addr);
> + data->length = input.length;
> + }
> +
> + ret = sev_issue_cmd(SEV_CMD_PEK_CSR, data, &argp->error);
> +
> + if (csr_addr) {
> + if (copy_to_user((void *)input.address, csr_addr,
> + input.length)) {
> + ret = -EFAULT;
> + goto e_free;
> + }
> + }
> +
> + input.length = data->length;
> + if (copy_to_user((void *)argp->data, &input,
> + sizeof(struct sev_user_data_pek_csr)))
> + ret = -EFAULT;
> +e_free:
> + if (do_shutdown)
> + sev_platform_shutdown(&error);
Who's looking at that error?
No one, AFAICT. It is a stack variable which gets destroyed after this
function returns. The other call sites look the same. Please fix.
> + kfree(csr_addr);
> + kfree(data);
> + return ret;
> +}
> +
> +static int sev_ioctl_pdh_gen(struct sev_issue_cmd *argp)
> +{
> + int ret, state, error, do_shutdown = 0;
> +
> + /*
> + * PDH_GEN command can be issued when platform is in INIT or WORKING
> + * state. If we are in UNINIT state then transition into INIT.
> + */
> + ret = sev_platform_get_state(&state, &argp->error);
> + if (ret)
> + return ret;
> +
> + if (state == SEV_STATE_UNINIT) {
> + /* transition the plaform into INIT state */
> + ret = __sev_platform_init(&argp->error);
> + if (ret)
> + return ret;
> + do_shutdown = 1;
> + }
> +
> + ret = sev_issue_cmd(SEV_CMD_PDH_GEN, 0, &argp->error);
> + if (do_shutdown)
> + sev_platform_shutdown(&error);
> + return ret;
> +}
> +
> +static int sev_ioctl_pek_gen(struct sev_issue_cmd *argp)
> +{
> + int do_shutdown = 0;
> + int error, ret, state;
> +
> + /*
> + * PEK_GEN command can be issued only when firmware is in INIT state.
> + * If firmware is in UNINIT state then we transition into INIT state
> + * and issue the command and then shutdown.
> + */
> + ret = sev_platform_get_state(&state, &argp->error);
> + if (ret)
> + return ret;
> +
> + if (state == SEV_STATE_UNINIT) {
> + /* transition the plaform into INIT state */
> + ret = __sev_platform_init(&argp->error);
> + if (ret)
> + return ret;
> +
> + do_shutdown = 1;
> + }
> +
> + ret = sev_issue_cmd(SEV_CMD_PEK_GEN, 0, &argp->error);
> +
> + if (do_shutdown)
> + sev_platform_shutdown(&error);
> + return ret;
> +}
> +
> +static int sev_ioctl_pek_cert_import(struct sev_issue_cmd *argp)
> +{
> + int ret, state, error, do_shutdown = 0;
> + struct sev_data_pek_cert_import *data;
> + struct sev_user_data_pek_cert_import input;
> + void *pek_cert = NULL, *oca_cert = NULL;
> +
> + if (copy_from_user(&input, (void *)argp->data, sizeof(*data)))
> + return -EFAULT;
> +
> + if (!input.pek_cert_address || !input.pek_cert_length ||
> + !input.oca_cert_address || !input.oca_cert_length)
> + return -EINVAL;
Here and everywhere else: please audit all those copy_from_user() calls
for user-controlled fields and the such and conservatively sanity-check
them. ou don't want to have security bugs in this driver!
You need to massage all those user values into sanity.
> + ret = sev_platform_get_state(&state, &argp->error);
> + if (ret)
> + return ret;
> +
> + /*
> + * CERT_IMPORT command can be issued only when platform is in INIT
> + * state. If we are in UNINIT state then transition into INIT state
> + * and issue the command.
> + */
> + if (state == SEV_STATE_UNINIT) {
> + /* transition platform init INIT state */
> + ret = __sev_platform_init(&argp->error);
> + if (ret)
> + return ret;
> + do_shutdown = 1;
> + }
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data) {
> + ret = -ENOMEM;
> + goto e_free;
> + }
> +
> + pek_cert = kmalloc(input.pek_cert_length, GFP_KERNEL);
> + if (!pek_cert) {
> + ret = -ENOMEM;
> + goto e_free;
> + }
> +
> + /* copy PEK certificate from userspace */
> + if (copy_from_user(pek_cert, (void *)input.pek_cert_address,
> + input.pek_cert_length)) {
> + ret = -EFAULT;
> + goto e_free;
> + }
> +
> + data->pek_cert_address = __psp_pa(pek_cert);
> + data->pek_cert_length = input.pek_cert_length;
> +
> + oca_cert = kmalloc(input.oca_cert_length, GFP_KERNEL);
There's your first overrun: that oca_cert_length you copy from userspace
and check only if it is 0 but it can be huuge.
> + if (!oca_cert) {
> + ret = -ENOMEM;
> + goto e_free;
> + }
> +
> + /* copy OCA certificate from userspace */
> + if (copy_from_user(oca_cert, (void *)input.oca_cert_address,
... and here's the user-controlled address where you copy the exploit
code from. You need to revisit all those copy_from_user() calls and be
absolutely defensive here.
> + input.oca_cert_length)) {
> + ret = -EFAULT;
> + goto e_free;
> + }
> +
> + data->oca_cert_address = __psp_pa(oca_cert);
> + data->oca_cert_length = input.oca_cert_length;
> +
> + ret = sev_issue_cmd(SEV_CMD_PEK_CERT_IMPORT, data, &argp->error);
> +e_free:
> + if (do_shutdown)
> + sev_platform_shutdown(&error);
> + kfree(oca_cert);
> + kfree(pek_cert);
> + kfree(data);
> + return ret;
> +}
> +
> +static int sev_ioctl_pdh_cert_export(struct sev_issue_cmd *argp)
> +{
> + int ret, state, error, need_shutdown = 0;
> + struct sev_data_pdh_cert_export *data;
> + struct sev_user_data_pdh_cert_export input;
> + void *pdh_cert = NULL, *cert_chain = NULL;
> +
> + if (copy_from_user(&input, (void *)argp->data, sizeof(*data)))
> + return -EFAULT;
> +
> + /*
> + * CERT_EXPORT command can be issued in INIT or WORKING state.
> + * If we are in UNINIT state then transition into INIT state and
> + * shutdown before exiting. But if platform is in WORKING state
> + * then EXPORT the certificate but do not shutdown the platform.
> + */
> + ret = sev_platform_get_state(&state, &argp->error);
> + if (ret)
> + return ret;
> +
> + if (state == SEV_STATE_UNINIT) {
> + ret = __sev_platform_init(&argp->error);
> + if (ret)
> + return ret;
> + need_shutdown = 1;
> + }
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data) {
> + ret = -ENOMEM;
> + goto e_free;
> + }
> +
> + if (input.pdh_cert_address) {
Oh wow, so that address is absolutely unchecked.
> + pdh_cert = kmalloc(input.pdh_cert_length, GFP_KERNEL);
> + if (!pdh_cert) {
> + ret = -ENOMEM;
> + goto e_free;
> + }
> +
> + data->pdh_cert_address = __psp_pa(pdh_cert);
> + data->pdh_cert_length = input.pdh_cert_length;
> + }
> +
> + if (input.cert_chain_address) {
Ditto.
And so on and so on...
> + cert_chain = kmalloc(input.cert_chain_length, GFP_KERNEL);
> + if (!cert_chain) {
> + ret = -ENOMEM;
> + goto e_free;
> + }
> +
> + data->cert_chain_address = __psp_pa(cert_chain);
> + data->cert_chain_length = input.cert_chain_length;
> + }
> +
> + ret = sev_issue_cmd(SEV_CMD_PDH_CERT_EXPORT, data, &argp->error);
> +
> + input.cert_chain_length = data->cert_chain_length;
> + input.pdh_cert_length = data->pdh_cert_length;
> +
> + /* copy PDH certificate to userspace */
> + if (pdh_cert) {
> + if (copy_to_user((void *)input.pdh_cert_address,
> + pdh_cert, input.pdh_cert_length)) {
> + ret = -EFAULT;
> + goto e_free;
> + }
> + }
> +
> + /* copy certificate chain to userspace */
> + if (cert_chain) {
> + if (copy_to_user((void *)input.cert_chain_address,
> + cert_chain, input.cert_chain_length)) {
> + ret = -EFAULT;
> + goto e_free;
> + }
> + }
> +
> + /* copy certificate length to userspace */
> + if (copy_to_user((void *)argp->data, &input,
> + sizeof(struct sev_user_data_pdh_cert_export)))
> + ret = -EFAULT;
> +
> +e_free:
> + if (need_shutdown)
> + sev_platform_shutdown(&error);
> +
> + kfree(cert_chain);
> + kfree(pdh_cert);
> + kfree(data);
> + return ret;
> +}
> +
> +static long sev_ioctl(struct file *file, unsigned int ioctl, unsigned long arg)
> +{
> + int ret = -EFAULT;
> + void __user *argp = (void __user *)arg;
> + struct sev_issue_cmd input;
> +
> + if (ioctl != SEV_ISSUE_CMD)
> + return -EINVAL;
> +
> + if (copy_from_user(&input, argp, sizeof(struct sev_issue_cmd)))
> + return -EFAULT;
> +
> + if (input.cmd > SEV_CMD_MAX)
> + return -EINVAL;
> +
> + switch (input.cmd) {
> +
> + case SEV_USER_CMD_FACTORY_RESET: {
> + ret = sev_ioctl_factory_reset(&input);
> + break;
> + }
> + case SEV_USER_CMD_PLATFORM_STATUS: {
> + ret = sev_ioctl_platform_status(&input);
> + break;
> + }
> + case SEV_USER_CMD_PEK_GEN: {
> + ret = sev_ioctl_pek_gen(&input);
> + break;
> + }
> + case SEV_USER_CMD_PDH_GEN: {
> + ret = sev_ioctl_pdh_gen(&input);
> + break;
> + }
> + case SEV_USER_CMD_PEK_CSR: {
> + ret = sev_ioctl_pek_csr(&input);
> + break;
> + }
> + case SEV_USER_CMD_PEK_CERT_IMPORT: {
> + ret = sev_ioctl_pek_cert_import(&input);
> + break;
> + }
> + case SEV_USER_CMD_PDH_CERT_EXPORT: {
> + ret = sev_ioctl_pdh_cert_export(&input);
> + break;
> + }
> + default:
> + ret = -EINVAL;
> + break;
> + }
> +
> + if (copy_to_user(argp, &input, sizeof(struct sev_issue_cmd)))
> + ret = -EFAULT;
> +
> + return ret;
> +}
> +
> +const struct file_operations sev_fops = {
> + .owner = THIS_MODULE,
> + .unlocked_ioctl = sev_ioctl,
> +};
> +
> +int sev_ops_init(struct sev_device *sev)
> +{
> + struct miscdevice *misc = &sev->misc;
> +
> + /* if sev device is already registered then do nothing */
> + if (sev_initialized)
> + return 0;
> +
> + misc->minor = MISC_DYNAMIC_MINOR;
> + misc->name = sev->name;
> + misc->fops = &sev_fops;
> + sev_initialized = true;
> +
> + return misc_register(misc);
> +}
> +
> +void sev_ops_destroy(struct sev_device *sev)
> +{
> + misc_deregister(&sev->misc);
> +}
> diff --git a/drivers/crypto/ccp/sp-pci.c b/drivers/crypto/ccp/sp-pci.c
> index e58b3ad..20a0f35 100644
> --- a/drivers/crypto/ccp/sp-pci.c
> +++ b/drivers/crypto/ccp/sp-pci.c
> @@ -280,7 +280,7 @@ static const struct sp_dev_vdata dev_vdata[] = {
> #ifdef CONFIG_CRYPTO_DEV_SP_CCP
> .ccp_vdata = &ccpv5a,
> #endif
> -#ifdef CONFIG_CRYPTO_DEV_PSP
> +#ifdef CONFIG_CRYPTO_DEV_SP_PSP
> .psp_vdata = &psp_entry
> #endif
> },
> diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
> new file mode 100644
> index 0000000..1736880
> --- /dev/null
> +++ b/include/linux/psp-sev.h
> @@ -0,0 +1,683 @@
> +/*
> + * AMD Secure Encrypted Virtualization (SEV) driver interface
> + *
> + * Copyright (C) 2016-2017 Advanced Micro Devices, Inc.
> + *
> + * Author: Brijesh Singh <[email protected]>
> + *
> + * 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.
<--- you probably should have a reference to the document containing all
those commands, i.e.,
http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
Also, for your next submission, please split this huuge patch. Those
definitions can go separately, for example.
> + */
> +
> +#ifndef __PSP_SEV_H__
> +#define __PSP_SEV_H__
> +
> +#ifdef CONFIG_X86
> +#include <linux/mem_encrypt.h>
> +
> +#define __psp_pa(x) __sme_pa(x)
> +#else
> +#define __psp_pa(x) __pa(x)
> +#endif
> +
> +/**
> + * SEV platform state
> + */
> +enum sev_state {
> + SEV_STATE_UNINIT = 0x0,
> + SEV_STATE_INIT = 0x1,
> + SEV_STATE_WORKING = 0x2,
> +
> + SEV_STATE_MAX
> +};
> +
> +/**
> + * SEV platform and guest management commands
> + */
> +enum sev_cmd {
> + /* platform commands */
> + SEV_CMD_INIT = 0x001,
> + SEV_CMD_SHUTDOWN = 0x002,
> + SEV_CMD_FACTORY_RESET = 0x003,
> + SEV_CMD_PLATFORM_STATUS = 0x004,
> + SEV_CMD_PEK_GEN = 0x005,
> + SEV_CMD_PEK_CSR = 0x006,
> + SEV_CMD_PEK_CERT_IMPORT = 0x007,
> + SEV_CMD_PDH_CERT_EXPORT = 0x008,
> + SEV_CMD_PDH_GEN = 0x009,
> + SEV_CMD_DF_FLUSH = 0x00A,
> +
> + /* Guest commands */
> + SEV_CMD_DECOMMISSION = 0x020,
> + SEV_CMD_ACTIVATE = 0x021,
> + SEV_CMD_DEACTIVATE = 0x022,
> + SEV_CMD_GUEST_STATUS = 0x023,
> +
> + /* Guest launch commands */
> + SEV_CMD_LAUNCH_START = 0x030,
> + SEV_CMD_LAUNCH_UPDATE_DATA = 0x031,
> + SEV_CMD_LAUNCH_UPDATE_VMSA = 0x032,
> + SEV_CMD_LAUNCH_MEASURE = 0x033,
> + SEV_CMD_LAUNCH_UPDATE_SECRET = 0x034,
> + SEV_CMD_LAUNCH_FINISH = 0x035,
> +
> + /* Guest migration commands (outgoing) */
> + SEV_CMD_SEND_START = 0x040,
> + SEV_CMD_SEND_UPDATE_DATA = 0x041,
> + SEV_CMD_SEND_UPDATE_VMSA = 0x042,
> + SEV_CMD_SEND_FINISH = 0x043,
> +
> + /* Guest migration commands (incoming) */
> + SEV_CMD_RECEIVE_START = 0x050,
> + SEV_CMD_RECEIVE_UPDATE_DATA = 0x051,
> + SEV_CMD_RECEIVE_UPDATE_VMSA = 0x052,
> + SEV_CMD_RECEIVE_FINISH = 0x053,
> +
> + /* Guest debug commands */
> + SEV_CMD_DBG_DECRYPT = 0x060,
> + SEV_CMD_DBG_ENCRYPT = 0x061,
> +
> + SEV_CMD_MAX,
> +};
> +
> +/**
> + * status code returned by the commands
> + */
> +enum psp_ret_code {
> + SEV_RET_SUCCESS = 0,
> + SEV_RET_INVALID_PLATFORM_STATE,
> + SEV_RET_INVALID_GUEST_STATE,
> + SEV_RET_INAVLID_CONFIG,
> + SEV_RET_INVALID_LENGTH,
> + SEV_RET_ALREADY_OWNED,
> + SEV_RET_INVALID_CERTIFICATE,
> + SEV_RET_POLICY_FAILURE,
> + SEV_RET_INACTIVE,
> + SEV_RET_INVALID_ADDRESS,
> + SEV_RET_BAD_SIGNATURE,
> + SEV_RET_BAD_MEASUREMENT,
> + SEV_RET_ASID_OWNED,
> + SEV_RET_INVALID_ASID,
> + SEV_RET_WBINVD_REQUIRED,
> + SEV_RET_DFFLUSH_REQUIRED,
> + SEV_RET_INVALID_GUEST,
> + SEV_RET_INVALID_COMMAND,
> + SEV_RET_ACTIVE,
> + SEV_RET_HWSEV_RET_PLATFORM,
> + SEV_RET_HWSEV_RET_UNSAFE,
> + SEV_RET_UNSUPPORTED,
> + SEV_RET_MAX,
> +};
> +
> +/**
> + * struct sev_data_init - INIT command parameters
> + *
> + * @flags: processing flags
> + * @tmr_address: system physical address used for SEV-ES
> + * @tmr_length: length of tmr_address
> + */
> +struct sev_data_init {
> + __u32 flags; /* In */
> + __u32 reserved; /* In */
> + __u64 tmr_address; /* In */
> + __u32 tmr_length; /* In */
> +};
I guess all those structs should be __packed to avoid the compiler
adding padding.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
Hi Boris,
I will address all your feedback in next rev.
On 09/12/2017 09:02 AM, Borislav Petkov wrote:
...
>
> You could make that more tabular like this:
>
> case SEV_CMD_INIT: return sizeof(struct sev_data_init);
> case SEV_CMD_PLATFORM_STATUS: return sizeof(struct sev_data_status);
> case SEV_CMD_PEK_CSR: return sizeof(struct sev_data_pek_csr);
> ...
>
> which should make it more readable.
>
> But looking at this more, this is a static mapping between the commands
> and the corresponding struct sizes and you use it in
>
> print_hex_dump_debug("(in): ", DUMP_PREFIX_OFFSET, 16, 2, data,
> sev_cmd_buffer_len(cmd), false);
>
> But then, I don't see what that brings you because you're not dumping
> the actual @data length but the *expected* data length based on the
> command type.
>
> And *that* you can look up in the manual and do not need it in code,
> enlarging the driver unnecessarily.
>
> ...
The debug statement is very helpful during development, it gives me the full
view of what command we send to PSP, data dump of command buffer before and
after the request completion. e.g when dyndbg is enabled the output looks like
this:
[392035.621308] ccp 0000:02:00.2: sev command id 0x4 buffer 0x000080146d232820
[392035.621312] (in): 00000000: 0000 0000 0000 0000 0000 0000
[392035.624725] (out): 00000000: 0e00 0000 0000 0b00 0000 0000
The first debug line prints command ID, second line prints memory dump of the command
structure and third line prints memory dump of command structure after PSP processed
the command.
The caller will use sev_issue_cmd() to issue PSP command. At this time we know
the command id and a opaque pointer (this points to command structure for command id).
Caller does not give us length of the command structure hence we need to derive it
from the command id using sev_cmd_buffer_len(). The command structure length is fixed
for a given command id.
Thanks
Brijesh
On Tue, Sep 12, 2017 at 10:32:13AM -0500, Brijesh Singh wrote:
> The debug statement is very helpful during development, it gives me the full
> view of what command we send to PSP, data dump of command buffer before and
> after the request completion. e.g when dyndbg is enabled the output looks like
> this:
I'm sure it is all very helpful but you have a bunch of code which is
always built-in and useful only to developers. Which means it could
be behind #ifdef DEBUG at least and disabled on production systems.
You don't have to do it immediately but once the stuff goes up and
everything stabilizes, you could ifdef it out... Something to think
about later, I'd say.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:41PM -0500, Brijesh Singh wrote:
> In current implementation, asid allocation starts from 1, this patch
Never say "This patch" in a commit message of a patch. It is
tautologically useless.
> adds a min_asid variable in svm_vcpu structure to allow starting asid
> from something other than 1.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> Reviewed-by: Paolo Bonzini <[email protected]>
> ---
> arch/x86/kvm/svm.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:42PM -0500, Brijesh Singh wrote:
> SEV-enabled guest must use ASIDs from the defined subset, while non-SEV
"A SEV-enabled ..."
> guests can use the remaining ASID range. The range of ASID allowed for
> SEV-enabled guest is from 1 to a maximum value defined via CPUID
> Fn8000_001f[ECX].
I'd rewrite that to:
"The range of allowed SEV guest ASIDs is [1 - CPUID_8000_001F[ECX][31:0]]".
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 23 ++++++++++++++++++++++-
> 1 file changed, 22 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 46f41bb..06bd902 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -319,6 +319,9 @@ enum {
>
> #define VMCB_AVIC_APIC_BAR_MASK 0xFFFFFFFFFF000ULL
>
> +/* Secure Encrypted Virtualization */
If anything, this comment should explain what that variable is.
Basically the comment you have in sev_hardware_setup() now.
> +static unsigned int max_sev_asid;
> +
> static inline void mark_all_dirty(struct vmcb *vmcb)
> {
> vmcb->control.clean = 0;
> @@ -769,7 +772,7 @@ static int svm_hardware_enable(void)
> sd->asid_generation = 1;
> sd->max_asid = cpuid_ebx(SVM_CPUID_FUNC) - 1;
> sd->next_asid = sd->max_asid + 1;
> - sd->min_asid = 1;
> + sd->min_asid = max_sev_asid + 1;
>
> gdt = get_current_gdt_rw();
> sd->tss_desc = (struct kvm_ldttss_desc *)(gdt + GDT_ENTRY_TSS);
> @@ -1033,6 +1036,21 @@ static int avic_ga_log_notifier(u32 ga_tag)
> return 0;
> }
>
> +static __init void sev_hardware_setup(void)
> +{
> + int nguests;
> +
> + /*
> + * Get maximum number of encrypted guest supported: Fn8001_001F[ECX]
> + * Bit 31:0: Number of supported guest
> + */
> + nguests = cpuid_ecx(0x8000001F);
> + if (!nguests)
> + return;
> +
> + max_sev_asid = nguests;
> +}
max_sev_asid is static and it is already initialized to 0 and thus this
function can be simplified to:
/*
* Get maximum number of encrypted guest supported: Fn8001_001F[ECX].
* [31:0]: Number of supported guests.
*/
static __init void sev_hardware_setup(void)
{
max_sev_asid = cpuid_ecx(0x8000001F);
}
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:43PM -0500, Brijesh Singh wrote:
> From: Tom Lendacky <[email protected]>
>
> Currently the nested_ctl variable in the vmcb_control_area structure is
> used to indicate nested paging support. The nested paging support field
> is actually defined as bit 0 of the field. In order to support a new
> feature flag the usage of the nested_ctl and nested paging support must
> be converted to operate on a single bit.
>
> Signed-off-by: Tom Lendacky <[email protected]>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/include/asm/svm.h | 2 ++
> arch/x86/kvm/svm.c | 7 ++++---
> 2 files changed, 6 insertions(+), 3 deletions(-)
Reviewed-by: Borislav Petkov <[email protected]>
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:44PM -0500, Brijesh Singh wrote:
> From: Tom Lendacky <[email protected]>
>
> Define the SEV enable bit for the VMCB control structure. The hypervisor
> will use this bit to enable SEV in the guest.
>
> Signed-off-by: Tom Lendacky <[email protected]>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/include/asm/svm.h | 1 +
> 1 file changed, 1 insertion(+)
>
> diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
> index a3d9e0b..0be01f9 100644
> --- a/arch/x86/include/asm/svm.h
> +++ b/arch/x86/include/asm/svm.h
> @@ -140,6 +140,7 @@ struct __attribute__ ((__packed__)) vmcb_control_area {
> #define SVM_VM_CR_SVM_DIS_MASK 0x0010ULL
>
> #define SVM_NESTED_CTL_NP_ENABLE BIT(0)
> +#define SVM_NESTED_CTL_SEV_ENABLE BIT(1)
>
> struct __attribute__ ((__packed__)) vmcb_seg {
> u16 selector;
> --
Reviewed-by: Borislav Petkov <[email protected]>
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:45PM -0500, Brijesh Singh wrote:
> This CPUID provides the memory encryption support information on
^
leaf
> AMD Platform. The complete description for CPUID leaf is available
".. on AMD. Its complete description is available in ..."
> in APM volume 2, Section 15.34
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/cpuid.c | 2 +-
> arch/x86/kvm/svm.c | 6 ++++++
> 2 files changed, 7 insertions(+), 1 deletion(-)
with the above addressed:
Reviewed-by: Borislav Petkov <[email protected]>
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:46PM -0500, Brijesh Singh wrote:
> If hardware supports encrypting then KVM_MEMORY_ENCRYPT_OP ioctl can
> be used by qemu to issue platform specific memory encryption commands.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> Reviewed-by: Paolo Bonzini <[email protected]>
> ---
> arch/x86/include/asm/kvm_host.h | 2 ++
> arch/x86/kvm/x86.c | 12 ++++++++++++
> include/uapi/linux/kvm.h | 2 ++
> 3 files changed, 16 insertions(+)
...
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 6cd63c1..ab3b711 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -1355,6 +1355,8 @@ struct kvm_s390_ucas_mapping {
> /* Available with KVM_CAP_S390_CMMA_MIGRATION */
> #define KVM_S390_GET_CMMA_BITS _IOWR(KVMIO, 0xb8, struct kvm_s390_cmma_log)
> #define KVM_S390_SET_CMMA_BITS _IOW(KVMIO, 0xb9, struct kvm_s390_cmma_log)
> +/* Memory Encryption Commands */
> +#define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xba, unsigned long)
I think this should be documented in
Documentation/virtual/kvm/api.txt... unless I'm missing that hunk in
some later patch...
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On 09/12/2017 03:04 PM, Borislav Petkov wrote:
...
>> SEV-enabled guest is from 1 to a maximum value defined via CPUID
>> Fn8000_001f[ECX].
>
> I'd rewrite that to:
>
> "The range of allowed SEV guest ASIDs is [1 - CPUID_8000_001F[ECX][31:0]]".
>
thanks, will do.
...
>>
>> +/* Secure Encrypted Virtualization */
>
> If anything, this comment should explain what that variable is.
> Basically the comment you have in sev_hardware_setup() now.
>
Will add more comments.
...
>
> max_sev_asid is static and it is already initialized to 0 and thus this
> function can be simplified to:
>
> /*
> * Get maximum number of encrypted guest supported: Fn8001_001F[ECX].
> * [31:0]: Number of supported guests.
> */
> static __init void sev_hardware_setup(void)
> {
> max_sev_asid = cpuid_ecx(0x8000001F);
> }
>
Agreed, I will improve it.
thanks
On Tue, Sep 12, 2017 at 03:24:05PM -0500, Brijesh Singh wrote:
> > If anything, this comment should explain what that variable is.
> > Basically the comment you have in sev_hardware_setup() now.
> >
>
> Will add more comments.
I didn't mean that - I meant that /* Secure Encrypted Virtualization */
as a comment doesn't fit over the variable max_sev_asid. If anything, it should
explain the variable or not be there at all.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:47PM -0500, Brijesh Singh wrote:
> If hardware support memory encryption then KVM_MEMORY_REGISTER_RAM and
supports
> KVM_MEMORY_UNREGISTER_RAM ioctl's can be used by userspace to register/
> unregister the guest memory regions which may contains the encrypted
"... which may contain encrypted data ... "
> data (e.g guest RAM, PCI BAR, SMRAM etc).
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/include/asm/kvm_host.h | 4 ++++
> arch/x86/kvm/x86.c | 36 ++++++++++++++++++++++++++++++++++++
> include/uapi/linux/kvm.h | 9 +++++++++
> 3 files changed, 49 insertions(+)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index 99a0e11..4295f82 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -1059,6 +1059,10 @@ struct kvm_x86_ops {
> void (*setup_mce)(struct kvm_vcpu *vcpu);
>
> int (*memory_encryption_op)(struct kvm *kvm, void __user *argp);
> + int (*memory_encryption_register_ram)(struct kvm *kvm,
> + struct kvm_memory_encrypt_ram *ram);
> + int (*memory_encryption_unregister_ram)(struct kvm *kvm,
> + struct kvm_memory_encrypt_ram *ram);
> };
You can shorten those prefixes to "mem_enc" or so and struct
kvm_memory_encrypt_ram to struct enc_region - which is exactly what it
is - an encrypted memory region descriptor - and then fit each function
on a single line.
>
> struct kvm_arch_async_pf {
> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index c9d3ff5..8febdb5 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -3982,6 +3982,24 @@ static int kvm_vm_ioctl_memory_encryption_op(struct kvm *kvm, void __user *argp)
> return -ENOTTY;
> }
>
> +static int kvm_vm_ioctl_mem_encrypt_register_ram(struct kvm *kvm,
> + struct kvm_memory_encrypt_ram *ram)
> +{
> + if (kvm_x86_ops->memory_encryption_register_ram)
> + return kvm_x86_ops->memory_encryption_register_ram(kvm, ram);
> +
> + return -ENOTTY;
> +}
> +
> +static int kvm_vm_ioctl_mem_encrypt_unregister_ram(struct kvm *kvm,
> + struct kvm_memory_encrypt_ram *ram)
> +{
> + if (kvm_x86_ops->memory_encryption_unregister_ram)
> + return kvm_x86_ops->memory_encryption_unregister_ram(kvm, ram);
> +
> + return -ENOTTY;
> +}
> +
> long kvm_arch_vm_ioctl(struct file *filp,
> unsigned int ioctl, unsigned long arg)
> {
> @@ -4246,6 +4264,24 @@ long kvm_arch_vm_ioctl(struct file *filp,
> r = kvm_vm_ioctl_memory_encryption_op(kvm, argp);
> break;
> }
> + case KVM_MEMORY_ENCRYPT_REGISTER_RAM: {
> + struct kvm_memory_encrypt_ram ram;
> +
> + r = -EFAULT;
> + if (copy_from_user(&ram, argp, sizeof(ram)))
> + goto out;
> + r = kvm_vm_ioctl_mem_encrypt_register_ram(kvm, &ram);
> + break;
> + }
> + case KVM_MEMORY_ENCRYPT_UNREGISTER_RAM: {
> + struct kvm_memory_encrypt_ram ram;
> +
> + r = -EFAULT;
> + if (copy_from_user(&ram, argp, sizeof(ram)))
As earlier, those ioctl functions need to check the user-supplied values.
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index ab3b711..6074065 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -1357,6 +1357,15 @@ struct kvm_s390_ucas_mapping {
> #define KVM_S390_SET_CMMA_BITS _IOW(KVMIO, 0xb9, struct kvm_s390_cmma_log)
> /* Memory Encryption Commands */
> #define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xba, unsigned long)
> +#define KVM_MEMORY_ENCRYPT_REGISTER_RAM _IOR(KVMIO, 0xbb, \
> + struct kvm_memory_encrypt_ram)
> +#define KVM_MEMORY_ENCRYPT_UNREGISTER_RAM _IOR(KVMIO, 0xbc, \
> + struct kvm_memory_encrypt_ram)
As with KVM_MEMORY_ENCRYPT_OP, those two need to be in the KVM API document.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On 09/12/2017 03:29 PM, Borislav Petkov wrote:
...
>> + int (*memory_encryption_unregister_ram)(struct kvm *kvm,
>> + struct kvm_memory_encrypt_ram *ram);
>> };
>
> You can shorten those prefixes to "mem_enc" or so and struct
> kvm_memory_encrypt_ram to struct enc_region - which is exactly what it
> is - an encrypted memory region descriptor - and then fit each function
> on a single line.
>
Sure, I can do that. In one of the feedback Paolo recommended
KVM_MEMORY_ENCRYPT_* ioctl name hence I tried to stick with the same name
for structure. I am flexible to use 'struct enc_region' but I personally
prefer to keep "mem" somewhere in the structure naming to indicate its for
*memory* encryption -- maybe struct kvm_mem_enc_region.
...
>> + struct kvm_memory_encrypt_ram)
>
> As with KVM_MEMORY_ENCRYPT_OP, those two need to be in the KVM API document.
>
Yes, I missed updating the Documentation/virtual/kvm/api.txt for these new
ioctls. I will update it.
On Tue, Sep 12, 2017 at 03:50:55PM -0500, Brijesh Singh wrote:
> Sure, I can do that. In one of the feedback Paolo recommended
> KVM_MEMORY_ENCRYPT_* ioctl name hence I tried to stick with the same name
> for structure. I am flexible to use 'struct enc_region' but I personally
> prefer to keep "mem" somewhere in the structure naming to indicate its for
> *memory* encryption -- maybe struct kvm_mem_enc_region.
I'd drop the "kvm_" prefix: struct mem_enc_region;
But I sense we're bikeshedding here and I'd leave it up to you guys.
My only intent was to shorten those so that they fit on a single line.
In general, I like using small struct names which do not look like
sentences and which give you code that reads quickly, at a glance. But
this is just me.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:48PM -0500, Brijesh Singh wrote:
> The patch adds a new member (sev_info) in 'struct kvm_arch', and
Never say "This patch" in a commit message of a patch. It is
tautologically useless.
> setter/getter functions for the sev_info field.
Also, I can see what the patch does from the hunk below. What is more
important to explain in the commit message is *why* you're doing what
you're doing.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/include/asm/kvm_host.h | 9 +++++++++
> arch/x86/kvm/svm.c | 45 +++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 54 insertions(+)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index 4295f82..150177e 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -742,6 +742,13 @@ enum kvm_irqchip_mode {
> KVM_IRQCHIP_SPLIT, /* created with KVM_CAP_SPLIT_IRQCHIP */
> };
>
> +struct kvm_sev_info {
> + bool active; /* SEV enabled guest */
> + unsigned int handle; /* firmware handle */
> + unsigned int asid; /* asid for this guest */
> + int sev_fd; /* SEV device fd */
> +};
> +
> struct kvm_arch {
> unsigned int n_used_mmu_pages;
> unsigned int n_requested_mmu_pages;
> @@ -829,6 +836,8 @@ struct kvm_arch {
>
> bool x2apic_format;
> bool x2apic_broadcast_quirk_disabled;
> +
> + struct kvm_sev_info sev_info;
> };
>
> struct kvm_vm_stat {
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 256c9df..2a5a03a 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -322,6 +322,51 @@ enum {
> /* Secure Encrypted Virtualization */
> static unsigned int max_sev_asid;
>
> +static inline struct kvm_sev_info *to_sev_info(struct kvm *kvm)
> +{
> + return &kvm->arch.sev_info;
> +}
> +
> +static inline void sev_set_active(struct kvm *kvm)
> +{
> + to_sev_info(kvm)->active = true;
> +}
Is this the accepted way to do this in KVM land or can you simply access
all members directly:
kvm->arch.sev_info.<member>
Because I see stuff like that:
static void kvm_gen_update_masterclock(struct kvm *kvm)
{
...
struct kvm_arch *ka = &kvm->arch;
spin_lock(&ka->pvclock_gtod_sync_lock);
and
struct kvm_lapic *apic = svm->vcpu.arch.apic;
...
kvm_lapic_reg_write(apic, APIC_ICR2, icrh);
so why do you need the accessors?
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:49PM -0500, Brijesh Singh wrote:
> Define Secure Encrypted Virtualization (SEV) key management command id
> and structure. The command definition is available in SEV KM [1] spec
> 0.14 and Documentation/virtual/kvm/amd-memory-encryption.txt
>
> [1] http://support.amd.com/TechDocs/55766_SEV-KM API_Specification.pdf
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> include/uapi/linux/kvm.h | 148 +++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 148 insertions(+)
>
> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
> index 6074065..8decc88 100644
> --- a/include/uapi/linux/kvm.h
> +++ b/include/uapi/linux/kvm.h
> @@ -1367,6 +1367,154 @@ struct kvm_memory_encrypt_ram {
> __u64 size;
> };
>
> +/* Secure Encrypted Virtualization command */
> +enum sev_cmd_id {
> + /* Guest initialization commands */
> + KVM_SEV_INIT = 0,
> + KVM_SEV_ES_INIT,
> + /* Guest launch commands */
> + KVM_SEV_LAUNCH_START,
> + KVM_SEV_LAUNCH_UPDATE_DATA,
> + KVM_SEV_LAUNCH_UPDATE_VMSA,
> + KVM_SEV_LAUNCH_SECRET,
> + KVM_SEV_LAUNCH_MEASURE,
> + KVM_SEV_LAUNCH_FINISH,
> + /* Guest migration commands (outgoing) */
> + KVM_SEV_SEND_START,
> + KVM_SEV_SEND_UPDATE_DATA,
> + KVM_SEV_SEND_UPDATE_VMSA,
> + KVM_SEV_SEND_FINISH,
> + /* Guest migration commands (incoming) */
> + KVM_SEV_RECEIVE_START,
> + KVM_SEV_RECEIVE_UPDATE_DATA,
> + KVM_SEV_RECEIVE_UPDATE_VMSA,
> + KVM_SEV_RECEIVE_FINISH,
> + /* Guest status and debug commands */
> + KVM_SEV_GUEST_STATUS,
> + KVM_SEV_DBG_DECRYPT,
> + KVM_SEV_DBG_ENCRYPT,
> + /* Guest certificates commands */
> + KVM_SEV_CERT_EXPORT,
> +
> + KVM_SEV_NR_MAX,
> +};
> +
> +struct kvm_sev_cmd {
> + __u32 id;
> + __u64 data;
> + __u32 error;
> + __u32 sev_fd;
> +};
As before, if those structs are read from/written to hardware/firmware,
they should be declared __packed so that the compiler doesn't add
padding.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:40PM -0500, Brijesh Singh wrote:
> AMDs new Secure Encrypted Virtualization (SEV) feature allows the memory
> contents of a virtual machine to be transparently encrypted with a key
> unique to the guest VM. The programming and management of the encryption
> keys are handled by the AMD Secure Processor (AMD-SP), which exposes the
> commands for these tasks. The complete spec for various commands are
> available at:
> http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
>
> This patch extends AMD-SP driver to provide:
>
> - a in-kernel APIs to communicate with SEV device. The APIs can be used
> by the hypervisor to create encryption context for the SEV guests.
>
> - a userspace IOCTL to manage the platform certificates etc
>
> Cc: Herbert Xu <[email protected]>
> Cc: David S. Miller <[email protected]>
> Cc: Gary Hook <[email protected]>
> Cc: [email protected]
> Signed-off-by: Brijesh Singh <[email protected]>
...
> +int sev_issue_cmd(int cmd, void *data, int *psp_ret)
> +{
> + struct sev_device *sev = get_sev_master_device();
> + unsigned int phys_lsb, phys_msb;
> + unsigned int reg, ret;
> +
> + if (!sev)
> + return -ENODEV;
> +
> + if (psp_ret)
> + *psp_ret = 0;
So you set psp_ret to 0 here...
> +
> + /* 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;
> +
> + dev_dbg(sev->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);
> +
> + /* Only one command at a time... */
> + mutex_lock(&sev_cmd_mutex);
> +
> + iowrite32(phys_lsb, sev->io_regs + PSP_CMDBUFF_ADDR_LO);
> + iowrite32(phys_msb, sev->io_regs + PSP_CMDBUFF_ADDR_HI);
> + wmb();
> +
> + reg = cmd;
> + reg <<= PSP_CMDRESP_CMD_SHIFT;
> + reg |= sev_poll ? 0 : PSP_CMDRESP_IOC;
> + iowrite32(reg, sev->io_regs + PSP_CMDRESP);
> +
> + ret = sev_wait_cmd(sev, ®);
> + if (ret)
> + goto unlock;
... something fails here and you unlock...
> +
> + if (psp_ret)
> + *psp_ret = reg & PSP_CMDRESP_ERR_MASK;
> +
> + if (reg & PSP_CMDRESP_ERR_MASK) {
> + dev_dbg(sev->dev, "sev command %u failed (%#010x)\n",
> + cmd, reg & PSP_CMDRESP_ERR_MASK);
> + ret = -EIO;
> + }
> +
> +unlock:
> + mutex_unlock(&sev_cmd_mutex);
> + print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data,
> + sev_cmd_buffer_len(cmd), false);
> + return ret;
... and here you return psp_ret == 0 even though something failed.
What I think you should do is not touch @psp_ret when you return before
the SEV command executes and *when* you return, set @psp_ret accordingly
to denote the status of the command execution.
Or if you're touching it before you execute the SEV
command and you return early, it should say something like
PSP_CMDRESP_COMMAND_DIDNT_EXECUTE or so, to tell the caller exactly what
happened.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:50PM -0500, Brijesh Singh wrote:
> The command initializes the SEV firmware and allocate a new ASID for
allocates
> this guest from SEV ASID pool. The firmware must be initialized before
from the
> we issue guest launch command to create a new encryption context.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 187 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 2a5a03a..e99a572 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -37,6 +37,8 @@
> #include <linux/amd-iommu.h>
> #include <linux/hashtable.h>
> #include <linux/frame.h>
> +#include <linux/psp-sev.h>
> +#include <linux/file.h>
>
> #include <asm/apic.h>
> #include <asm/perf_event.h>
> @@ -321,6 +323,14 @@ enum {
>
> /* Secure Encrypted Virtualization */
> static unsigned int max_sev_asid;
> +static unsigned long *sev_asid_bitmap;
> +static int sev_asid_new(void);
This forward declaration is superfluous.
> +static void sev_asid_free(int asid);
You can move the sev_asid* code up and thus get rid of this forward
declaration too.
And also, svm.c is really huge. We probably should think about splitting
it in logical pieces... But that's for another day.
> +
> +static bool svm_sev_enabled(void)
> +{
> + return !!max_sev_asid;
You don't need the "!!".
> +}
>
> static inline struct kvm_sev_info *to_sev_info(struct kvm *kvm)
> {
> @@ -1093,6 +1103,12 @@ static __init void sev_hardware_setup(void)
> if (!nguests)
> return;
>
> + /* Initialize SEV ASID bitmap */
> + sev_asid_bitmap = kcalloc(BITS_TO_LONGS(nguests),
> + sizeof(unsigned long), GFP_KERNEL);
> + if (IS_ERR(sev_asid_bitmap))
> + return;
> +
> max_sev_asid = nguests;
> }
>
> @@ -1184,10 +1200,18 @@ static __init int svm_hardware_setup(void)
> return r;
> }
>
> +static __exit void sev_hardware_unsetup(void)
> +{
> + kfree(sev_asid_bitmap);
> +}
> +
> static __exit void svm_hardware_unsetup(void)
"unsetup" - oh my. :)
> int cpu;
>
> + if (svm_sev_enabled())
> + sev_hardware_unsetup();
> +
> for_each_possible_cpu(cpu)
> svm_cpu_uninit(cpu);
>
> @@ -1373,6 +1397,9 @@ static void init_vmcb(struct vcpu_svm *svm)
> svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK;
> }
>
> + if (sev_guest(svm->vcpu.kvm))
> + svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE;
> +
> mark_all_dirty(svm->vmcb);
>
> enable_gif(svm);
> @@ -1483,6 +1510,51 @@ static inline int avic_free_vm_id(int id)
> return 0;
> }
>
> +static int sev_platform_get_state(int *state, int *error)
> +{
> + int ret;
> + struct sev_data_status *data;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
It's a bit silly to do the allocation only for the duration of
sev_platform_status() - just allocate "data" on the stack.
> + if (!data)
> + return -ENOMEM;
> +
> + ret = sev_platform_status(data, error);
> + if (!ret)
> + *state = data->state;
> +
> + kfree(data);
> + return ret;
> +}
> +
> +static void sev_firmware_uninit(void)
I guess sev_firmware_exit() or so.
> +{
> + int rc, state, error;
> +
> + rc = sev_platform_get_state(&state, &error);
> + if (rc) {
> + pr_err("SEV: failed to get firmware state (%#x)\n",
> + error);
error fits on the same line.
> + return;
> + }
> +
> + /* If we are in initialized state then uninitialize it */
> + if (state == SEV_STATE_INIT)
> + sev_platform_shutdown(&error);
> +
> +}
> +
> +static void sev_vm_destroy(struct kvm *kvm)
> +{
> + int state, error;
arch/x86/kvm/svm.c: In function ‘sev_vm_destroy’:
arch/x86/kvm/svm.c:1548:13: warning: unused variable ‘error’ [-Wunused-variable]
int state, error;
^~~~~
arch/x86/kvm/svm.c:1548:6: warning: unused variable ‘state’ [-Wunused-variable]
int state, error;
^~~~~
> +
> + if (!sev_guest(kvm))
> + return;
> +
> + sev_asid_free(sev_get_asid(kvm));
> + sev_firmware_uninit();
I think you want to destroy the firmware context first and then free the
asid.
> +}
> +
> static void avic_vm_destroy(struct kvm *kvm)
> {
> unsigned long flags;
> @@ -1503,6 +1575,12 @@ static void avic_vm_destroy(struct kvm *kvm)
> spin_unlock_irqrestore(&svm_vm_data_hash_lock, flags);
> }
>
> +static void svm_vm_destroy(struct kvm *kvm)
> +{
> + avic_vm_destroy(kvm);
> + sev_vm_destroy(kvm);
> +}
> +
> static int avic_vm_init(struct kvm *kvm)
> {
> unsigned long flags;
> @@ -5428,6 +5506,112 @@ static void svm_setup_mce(struct kvm_vcpu *vcpu)
> vcpu->arch.mcg_cap &= 0x1ff;
> }
>
> +static int sev_asid_new(void)
> +{
> + int pos;
> +
> + if (!max_sev_asid)
if (!svm_sev_enabled())
since you have an accessor.
> + return -EINVAL;
> +
> + pos = find_first_zero_bit(sev_asid_bitmap, max_sev_asid);
> + if (pos >= max_sev_asid)
> + return -EBUSY;
> +
> + set_bit(pos, sev_asid_bitmap);
> + return pos + 1;
> +}
> +
> +static void sev_asid_free(int asid)
> +{
> + int pos;
if (!svm_sev_enabled())
return;
> +
> + pos = asid - 1;
> + clear_bit(pos, sev_asid_bitmap);
> +}
> +
> +static int sev_firmware_init(int *error)
> +{
> + int ret, state;
> +
> + ret = sev_platform_get_state(&state, error);
> + if (ret)
> + return ret;
> +
> + /*
> + * If SEV firmware is in uninitialized state, lets initialize the
> + * firmware before issuing guest commands.
> + */
> + if (state == SEV_STATE_UNINIT) {
> + struct sev_data_init *data;
Same note as above - allocate data on the stack.
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + ret = sev_platform_init(data, error);
> + kfree(data);
> + }
> +
> + return ret;
> +}
> +
> +static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + int asid, ret;
> + struct fd f;
> +
> + f = fdget(argp->sev_fd);
> + if (!f.file)
> + return -EBADF;
> +
> + /* initialize the SEV firmware */
> + ret = sev_firmware_init(&argp->error);
> + if (ret)
> + goto e_err;
> +
> + /* allocate asid from SEV pool */
> + ret = -ENOTTY;
> + asid = sev_asid_new();
> + if (asid < 0) {
> + pr_err("SEV: failed to get free asid\n");
> + sev_platform_shutdown(&argp->error);
> + goto e_err;
> + }
> +
> + sev_set_active(kvm);
I think that should be the last thing you execute before returning.
> + sev_set_asid(kvm, asid);
> + sev_set_fd(kvm, argp->sev_fd);
> + ret = 0;
> +e_err:
> + fdput(f);
> + return ret;
> +}
> +
> +static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
> +{
> + struct kvm_sev_cmd sev_cmd;
> + int r = -ENOTTY;
> +
> + if (copy_from_user(&sev_cmd, argp, sizeof(struct kvm_sev_cmd)))
> + return -EFAULT;
Sanity-check that sev_cmd.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On 09/13/2017 08:37 AM, Borislav Petkov wrote:
...
>> + return &kvm->arch.sev_info;
>> +}
>> +
>> +static inline void sev_set_active(struct kvm *kvm)
>> +{
>> + to_sev_info(kvm)->active = true;
>> +}
>
> Is this the accepted way to do this in KVM land or can you simply access
> all members directly:
>
> kvm->arch.sev_info.<member>
>
> Because I see stuff like that:
>
Actually, I see both approaches used in svm.c but I am flexible to go with
either ways. lets wait for Paolo and Radim comments.
-Brijesh
On 09/13/2017 09:17 AM, Borislav Petkov wrote:
...
>> +
>> +unlock:
>> + mutex_unlock(&sev_cmd_mutex);
>> + print_hex_dump_debug("(out): ", DUMP_PREFIX_OFFSET, 16, 2, data,
>> + sev_cmd_buffer_len(cmd), false);
>> + return ret;
>
> ... and here you return psp_ret == 0 even though something failed.
>
> What I think you should do is not touch @psp_ret when you return before
> the SEV command executes and *when* you return, set @psp_ret accordingly
> to denote the status of the command execution.
>
> Or if you're touching it before you execute the SEV
> command and you return early, it should say something like
> PSP_CMDRESP_COMMAND_DIDNT_EXECUTE or so, to tell the caller exactly what
> happened.
>
Agreed, very good catch thank you. I will fix it.
-Brijesh
On Wed, Sep 13, 2017 at 10:14:20AM -0500, Brijesh Singh wrote:
> Actually, I see both approaches used in svm.c but I am flexible to go with
> either ways. lets wait for Paolo and Radim comments.
Frankly, if you're going to access those fields only in svm.c, those
accessors look like an overkill to me. But maybe there's a higher
strategy behind it...
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:51PM -0500, Brijesh Singh wrote:
> SEV hardware uses ASIDs to associate memory encryption key with the
> guest VMs. During the guest creation time, we use SEV_CMD_ACTIVATE
"VM"
> command to bind a particular ASID to the guest. Lets make sure that
> VMCB is programmed with the binded ASID before a VMRUN.
"the VMCB" binded? you mean "bound"
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 49 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index e99a572..72f7c27 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -213,6 +213,9 @@ struct vcpu_svm {
> */
> struct list_head ir_list;
> spinlock_t ir_list_lock;
> +
> + /* which host cpu was used for running this vcpu */
s/cpu/CPU/
> + unsigned int last_cpuid;
... and since it is a CPU, then "last_cpu" I guess.
> };
>
> /*
> @@ -573,6 +576,8 @@ struct svm_cpu_data {
> struct kvm_ldttss_desc *tss_desc;
>
> struct page *save_area;
> +
> + struct vmcb **sev_vmcbs; /* index = sev_asid, value = vmcb pointer */
Put that comment above it.
> static DEFINE_PER_CPU(struct svm_cpu_data *, svm_data);
> @@ -886,6 +891,7 @@ static void svm_cpu_uninit(int cpu)
> return;
>
> per_cpu(svm_data, raw_smp_processor_id()) = NULL;
> + kfree(sd->sev_vmcbs);
> __free_page(sd->save_area);
> kfree(sd);
> }
> @@ -904,6 +910,14 @@ static int svm_cpu_init(int cpu)
> if (!sd->save_area)
> goto err_1;
>
> + if (svm_sev_enabled()) {
> + sd->sev_vmcbs = kmalloc((max_sev_asid + 1) * sizeof(void *),
> + GFP_KERNEL);
You can let that line stick out.
> + r = -ENOMEM;
That assignment usually is done before the call. I know, this function
does it differently but you can fix it up while you're touching it.
> + if (!sd->sev_vmcbs)
> + goto err_1;
> + }
> +
> per_cpu(svm_data, cpu) = sd;
>
> return 0;
> @@ -4442,12 +4456,40 @@ static void reload_tss(struct kvm_vcpu *vcpu)
> load_TR_desc();
> }
>
> +static void pre_sev_run(struct vcpu_svm *svm)
> +{
> + int cpu = raw_smp_processor_id();
> + int asid = sev_get_asid(svm->vcpu.kvm);
> + struct svm_cpu_data *sd = per_cpu(svm_data, cpu);
> +
> + /* Assign the asid allocated with this SEV guest */
> + svm->vmcb->control.asid = asid;
> +
> + /*
> + * Flush guest TLB:
> + *
> + * 1) when different VMCB for the same ASID is to be run on the same host CPU.
> + * 2) or this VMCB was executed on different host cpu in previous VMRUNs.
s/cpu/CPU/
> + */
> + if (sd->sev_vmcbs[asid] == svm->vmcb &&
> + svm->last_cpuid == cpu)
> + return;
> +
> + svm->last_cpuid = cpu;
> + sd->sev_vmcbs[asid] = svm->vmcb;
> + svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ASID;
> + mark_dirty(svm->vmcb, VMCB_ASID);
> +}
> +
> static void pre_svm_run(struct vcpu_svm *svm)
> {
> int cpu = raw_smp_processor_id();
>
> struct svm_cpu_data *sd = per_cpu(svm_data, cpu);
>
> + if (sev_guest(svm->vcpu.kvm))
> + return pre_sev_run(svm);
Just pass @cpu here so that you don't have to do raw_smp_processor_id()
again above.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
Hi Boris,
thanks for the detail review.
On 09/13/2017 10:06 AM, Borislav Petkov wrote:
...
>> +static int sev_platform_get_state(int *state, int *error)
>> +{
>> + int ret;
>> + struct sev_data_status *data;
>> +
>> + data = kzalloc(sizeof(*data), GFP_KERNEL);
>
> It's a bit silly to do the allocation only for the duration of
> sev_platform_status() - just allocate "data" on the stack.
>
I am okay with moving it on the stack but just to give context why
I went in this way. The physical address of data is given to the
device (in this case SEV FW). I was not sure if its okay to pass the
stack address to the device. Additionally, the FW spec requires us to
zero all the fields -- so we need to memset if we allocate it on the
stack.
On Wed, Sep 13, 2017 at 11:23:26AM -0500, Brijesh Singh wrote:
> I am okay with moving it on the stack but just to give context why
> I went in this way. The physical address of data is given to the
> device (in this case SEV FW). I was not sure if its okay to pass the
> stack address to the device.
Why would it not be ok?
> Additionally, the FW spec requires us to zero all the fields -- so we
> need to memset if we allocate it on the stack.
What do you think kzalloc() does internally? :)
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:52PM -0500, Brijesh Singh wrote:
> The command is used to bootstrap SEV guest from unencrypted boot images.
> The command creates a new VM encryption key (VEK) using the guest owner's
s/The command/It/
> policy, public DH certificates, and session information.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 165 insertions(+)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 72f7c27..3e325578 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -329,6 +329,8 @@ static unsigned int max_sev_asid;
> static unsigned long *sev_asid_bitmap;
> static int sev_asid_new(void);
> static void sev_asid_free(int asid);
> +static void sev_deactivate_handle(struct kvm *kvm, int *error);
> +static void sev_decommission_handle(struct kvm *kvm, int *error);
Please move code in a way that you don't need those forward
declarations. Also, I'm wondering if having all the SEV-related code
could live in sev.c or so - svm.c is humongous.
> static bool svm_sev_enabled(void)
> {
> @@ -1565,6 +1567,12 @@ static void sev_vm_destroy(struct kvm *kvm)
> if (!sev_guest(kvm))
> return;
>
> + /* release the firmware resources for this guest */
> + if (sev_get_handle(kvm)) {
> + sev_deactivate_handle(kvm, &error);
> + sev_decommission_handle(kvm, &error);
> + }
> +
> sev_asid_free(sev_get_asid(kvm));
> sev_firmware_uninit();
> }
> @@ -5635,6 +5643,159 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return ret;
> }
>
> +static int sev_issue_cmd(struct kvm *kvm, int id, void *data, int *error)
> +{
> + int fd = sev_get_fd(kvm);
> + struct fd f;
> + int ret;
> +
> + f = fdget(fd);
> + if (!f.file)
> + return -EBADF;
> +
> + ret = sev_issue_cmd_external_user(f.file, id, data, error);
> + fdput(f);
> +
> + return ret;
> +}
> +
> +static void sev_decommission_handle(struct kvm *kvm, int *error)
> +{
> + struct sev_data_decommission *data;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
Also, better on stack. Please do that for the other functions below too.
> + if (!data)
> + return;
> +
> + data->handle = sev_get_handle(kvm);
> + sev_guest_decommission(data, error);
> + kfree(data);
> +}
> +
> +static void sev_deactivate_handle(struct kvm *kvm, int *error)
> +{
> + struct sev_data_deactivate *data;
> + int ret;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return;
> +
> + data->handle = sev_get_handle(kvm);
> + ret = sev_guest_deactivate(data, error);
> + if (ret)
> + goto e_free;
> +
> + wbinvd_on_all_cpus();
> +
> + sev_guest_df_flush(error);
> +e_free:
> + kfree(data);
> +}
> +
> +static int sev_activate_asid(struct kvm *kvm, unsigned int handle, int *error)
> +{
> + struct sev_data_activate *data;
> + int asid = sev_get_asid(kvm);
> + int ret;
> +
> + wbinvd_on_all_cpus();
> +
> + ret = sev_guest_df_flush(error);
> + if (ret)
> + return ret;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->handle = handle;
> + data->asid = asid;
> + ret = sev_guest_activate(data, error);
> + if (ret)
> + goto e_err;
> +
> + sev_set_handle(kvm, handle);
> +e_err:
> + kfree(data);
> + return ret;
> +}
> +
> +static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + struct sev_data_launch_start *start = NULL;
> + struct kvm_sev_launch_start params;
> + void *dh_cert_addr = NULL;
> + void *session_addr = NULL;
> + int *error = &argp->error;
> + int ret;
> +
> + if (!sev_guest(kvm))
> + return -ENOTTY;
> +
> + ret = -EFAULT;
> + if (copy_from_user(¶ms, (void *)argp->data,
> + sizeof(struct kvm_sev_launch_start)))
Sanity-check params. This is especially important if later we start
using reserved fields.
> + goto e_free;
> +
> + ret = -ENOMEM;
> + start = kzalloc(sizeof(*start), GFP_KERNEL);
> + if (!start)
> + goto e_free;
> +
> + /* Bit 15:6 reserved, must be 0 */
> + start->policy = params.policy & ~0xffc0;
> +
> + if (params.dh_cert_length && params.dh_cert_address) {
Yeah, we talked about this already: sanity-checking needed. But you get
the idea.
> + ret = -ENOMEM;
> + dh_cert_addr = kmalloc(params.dh_cert_length, GFP_KERNEL);
> + if (!dh_cert_addr)
> + goto e_free;
> +
> + ret = -EFAULT;
> + if (copy_from_user(dh_cert_addr, (void *)params.dh_cert_address,
> + params.dh_cert_length))
> + goto e_free;
> +
> + start->dh_cert_address = __sme_set(__pa(dh_cert_addr));
> + start->dh_cert_length = params.dh_cert_length;
> + }
> +
> + if (params.session_length && params.session_address) {
> + ret = -ENOMEM;
> + session_addr = kmalloc(params.session_length, GFP_KERNEL);
> + if (!session_addr)
> + goto e_free;
> +
> + ret = -EFAULT;
> + if (copy_from_user(session_addr, (void *)params.session_address,
> + params.session_length))
if (copy_from_user(session_addr,
(void *)params.session_address,
params.session_length))
reads better to me. Better yet if you shorten those member names into
s_addr and s_len and so on...
--
Regards/Gruss,
Boris.
Good mailing practices for 400: avoid top-posting and trim the reply.
On Mon, Jul 24, 2017 at 03:02:53PM -0500, Brijesh Singh wrote:
> The command is used for encrypting the guest memory region using the VM
> encryption key (VEK) created during LAUNCH_START.
Yap, this is one good commit message!
:-)
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 165 insertions(+)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 3e325578..91b070f 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -39,6 +39,8 @@
> #include <linux/frame.h>
> #include <linux/psp-sev.h>
> #include <linux/file.h>
> +#include <linux/pagemap.h>
> +#include <linux/swap.h>
>
> #include <asm/apic.h>
> #include <asm/perf_event.h>
> @@ -331,6 +333,7 @@ static int sev_asid_new(void);
> static void sev_asid_free(int asid);
> static void sev_deactivate_handle(struct kvm *kvm, int *error);
> static void sev_decommission_handle(struct kvm *kvm, int *error);
> +#define __sme_page_pa(x) __sme_set(page_to_pfn(x) << PAGE_SHIFT)
>
> static bool svm_sev_enabled(void)
> {
> @@ -5796,6 +5799,164 @@ static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return ret;
> }
>
> +static struct page **sev_pin_memory(unsigned long uaddr, unsigned long ulen,
> + unsigned long *n, int write)
> +{
> + unsigned long npages, pinned, size;
> + struct page **pages;
> + int first, last;
> +
> + /* Get number of pages */
> + first = (uaddr & PAGE_MASK) >> PAGE_SHIFT;
> + last = ((uaddr + ulen - 1) & PAGE_MASK) >> PAGE_SHIFT;
> + npages = (last - first + 1);
> +
> + /* Avoid using vmalloc for smaller buffer */
> + size = npages * sizeof(struct page *);
> + if (size > PAGE_SIZE)
> + pages = vmalloc(size);
> + else
> + pages = kmalloc(size, GFP_KERNEL);
> +
> + if (!pages)
> + return NULL;
> +
> + /* pin the user virtual address */
> + pinned = get_user_pages_fast(uaddr, npages, write ? FOLL_WRITE : 0,
> + pages);
Let it stick out.
> + if (pinned != npages) {
> + pr_err("failed to pin %ld pages (got %ld)\n", npages, pinned);
> + goto err;
> + }
> +
> + *n = npages;
> + return pages;
> +err:
> + if (pinned > 0)
> + release_pages(pages, pinned, 0);
<---- newline here.
> + kvfree(pages);
> +
> + return NULL;
> +}
> +
> +static void sev_unpin_memory(struct page **pages, unsigned long npages)
> +{
> + release_pages(pages, npages, 0);
> + kvfree(pages);
> +}
> +
> +static void sev_clflush_pages(struct page *pages[], unsigned long npages)
> +{
> + uint8_t *page_virtual;
> + unsigned long i;
> +
> + if (npages == 0 || pages == NULL)
> + return;
> +
> + for (i = 0; i < npages; i++) {
> + page_virtual = kmap_atomic(pages[i]);
> + clflush_cache_range(page_virtual, PAGE_SIZE);
> + kunmap_atomic(page_virtual);
> + }
> +}
> +
> +static int get_num_contig_pages(int idx, struct page **inpages,
> + unsigned long npages)
> +{
> + int i = idx + 1, pages = 1;
> + unsigned long paddr, next_paddr;
> +
> + /* find the number of contiguous pages starting from idx */
> + paddr = __sme_page_pa(inpages[idx]);
> + while (i < npages) {
> + next_paddr = __sme_page_pa(inpages[i++]);
> + if ((paddr + PAGE_SIZE) == next_paddr) {
> + pages++;
> + paddr = next_paddr;
> + continue;
> + }
> + break;
> + }
> +
> + return pages;
> +}
> +
> +static int sev_launch_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + unsigned long vaddr, vaddr_end, next_vaddr, npages, size;
> + struct kvm_sev_launch_update_data params;
> + struct sev_data_launch_update_data *data;
> + struct page **inpages;
> + int i, ret, pages;
> +
> + if (!sev_guest(kvm))
> + return -ENOTTY;
> +
> + if (copy_from_user(¶ms, (void *)argp->data,
> + sizeof(struct kvm_sev_launch_update_data)))
> + return -EFAULT;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
Same issues as before.
> +
> + vaddr = params.address;
> + size = params.length;
> + vaddr_end = vaddr + size;
> +
> + /* lock the user memory */
> + inpages = sev_pin_memory(vaddr, size, &npages, 1);
This way user basically controls how many pages to pin and you need to
limit that on the upper end.
> + if (!inpages) {
> + ret = -ENOMEM;
> + goto e_free;
> + }
> +
> + /*
> + * invalidate the cache to ensure that DRAM has recent content before
recent content?
> + * calling the SEV commands.
> + */
> + sev_clflush_pages(inpages, npages);
> +
> + for (i = 0; vaddr < vaddr_end; vaddr = next_vaddr, i += pages) {
> + int offset, len;
> +
> + /*
> + * since user buffer may not be page aligned, calculate the
> + * offset within the page.
> + */
> + offset = vaddr & (PAGE_SIZE - 1);
> +
> + /*
> + * calculate the number of pages that can be encrypted in one go
> + */
> + pages = get_num_contig_pages(i, inpages, npages);
> +
> + len = min_t(size_t, ((pages * PAGE_SIZE) - offset), size);
> +
> + data->handle = sev_get_handle(kvm);
> + data->length = len;
> + data->address = __sme_page_pa(inpages[i]) + offset;
> + ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_DATA, data,
> + &argp->error);
Yah, let it stick out.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On 09/13/2017 12:25 PM, Borislav Petkov wrote:
...
>> +static void sev_deactivate_handle(struct kvm *kvm, int *error);
>> +static void sev_decommission_handle(struct kvm *kvm, int *error);
>
> Please move code in a way that you don't need those forward
> declarations. Also, I'm wondering if having all the SEV-related code
> could live in sev.c or so - svm.c is humongous.
>
Yes, svm.c is humongous.
...
>> +
>> +static void sev_decommission_handle(struct kvm *kvm, int *error)
>> +{
>> + struct sev_data_decommission *data;
>> +
>> + data = kzalloc(sizeof(*data), GFP_KERNEL);
>
> Also, better on stack. Please do that for the other functions below too.
Yes, some structures are small and I don't expect them to grow in newer API
spec. We should be able to move them on the stack. I will audit the code and
make the necessary changes.
....
>> + ret = -EFAULT;
>> + if (copy_from_user(¶ms, (void *)argp->data,
>> + sizeof(struct kvm_sev_launch_start)))
>
> Sanity-check params. This is especially important if later we start
> using reserved fields.
>
Yes, I will add some upper bound check on the length field and add the
sanity-check just after copying the parameters from userspace
...
>> + goto e_free;
>> +
>> + ret = -ENOMEM;
>> + start = kzalloc(sizeof(*start), GFP_KERNEL);
>> + if (!start)
>> + goto e_free;
>> +
>> + /* Bit 15:6 reserved, must be 0 */
>> + start->policy = params.policy & ~0xffc0;
>> +
>> + if (params.dh_cert_length && params.dh_cert_address) {
>
> Yeah, we talked about this already: sanity-checking needed. But you get
> the idea.
>
Will do
...
>
> if (copy_from_user(session_addr,
> (void *)params.session_address,
> params.session_length))
>
> reads better to me. Better yet if you shorten those member names into
> s_addr and s_len and so on...
>
>
Will use your recommendation.
thanks
On Wed, Sep 13, 2017 at 01:23:08PM -0500, Brijesh Singh wrote:
> Yes, I will add some upper bound check on the length field and add the
> sanity-check just after copying the parameters from userspace
Also, you could either fail the command if some of the reserved fields
are set - picky - or zero them out - less picky :)
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On 09/13/2017 01:37 PM, Borislav Petkov wrote:
> On Wed, Sep 13, 2017 at 01:23:08PM -0500, Brijesh Singh wrote:
>> Yes, I will add some upper bound check on the length field and add the
>> sanity-check just after copying the parameters from userspace
>
> Also, you could either fail the command if some of the reserved fields
> are set - picky - or zero them out - less picky :)
>
Actually reversed fields are not exposed in userspace structure.
e.g a LAUNCH_UPDATE_DATE userspace structure looks like this:
struct kvm_sev_launch_update_data {
__u64 address; /* userspace address of memory region to encrypt */
__u32 length; /* length of memory region to encrypt */
};
But SEV firmware command structure is a slightly different (mainly it contains
the reserved field and firmware handle etc).
/**
* struct sev_data_launch_update_data - LAUNCH_UPDATE_DATA command parameter
*
* @handle: firmware handle to use
* @length: length of memory to be encrypted
* @address: physical address of memory region to encrypt
*/
struct sev_data_launch_update_data {
u32 handle; /* In */
u32 reserved;
u64 address; /* In */
u32 length; /* In */
};
Please note that some commands require us passing the VM ASID etc --
userspace does not have VM ASID information.
The current approach is -- while handling the command we copy the value
from userspace structure into FW compatible structure and also populate
missing fields which are not known to userspace (e.g firmware handle,
VM ASID, use system physical addresses etc).
On 09/13/2017 12:55 PM, Borislav Petkov wrote:
...
>> +
>> + /* pin the user virtual address */
>> + pinned = get_user_pages_fast(uaddr, npages, write ? FOLL_WRITE : 0,
>> + pages);
>
> Let it stick out.
Will do.
...
>> + vaddr = params.address;
>> + size = params.length;
>> + vaddr_end = vaddr + size;
>> +
>> + /* lock the user memory */
>> + inpages = sev_pin_memory(vaddr, size, &npages, 1);
>
> This way user basically controls how many pages to pin and you need to
> limit that on the upper end.
>
Actually I don't know what should be sane upper bound in this case --
typically we encrypt the guest BIOS using LAUNCH_UPDATE_DATA command.
I have heard that some user may want to create a pre-encrypted image
(which may contains guest BIOS + kernel + initrd) -- this can be huge.
For SEV guest, we have been needing to pin the memory hence how about if
we limit the number of pages to pin with rlimit ? The rlimit check can
also include the guest RAM pinning.
>> + if (!inpages) {
>> + ret = -ENOMEM;
>> + goto e_free;
>> + }
>> +
>> + /*
>> + * invalidate the cache to ensure that DRAM has recent content before
>
> recent content?
Cache access from the PSP are coherent with x86 but not other way around --
I will update the comments to reflect the true meaning.
...
>
> Yah, let it stick out.
>
Okay.
On Wed, Sep 13, 2017 at 01:58:31PM -0500, Brijesh Singh wrote:
> Actually reversed fields are not exposed in userspace structure.
Ok.
> The current approach is -- while handling the command we copy the value
> from userspace structure into FW compatible structure and also populate
> missing fields which are not known to userspace (e.g firmware handle,
> VM ASID, use system physical addresses etc).
Yap, that makes sense. All I'm saying is, check anything that userspace
can influence and make sure it is sensible.
Thx.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Wed, Sep 13, 2017 at 02:45:37PM -0500, Brijesh Singh wrote:
> Actually I don't know what should be sane upper bound in this case --
> typically we encrypt the guest BIOS using LAUNCH_UPDATE_DATA command.
> I have heard that some user may want to create a pre-encrypted image
> (which may contains guest BIOS + kernel + initrd) -- this can be huge.
>
> For SEV guest, we have been needing to pin the memory hence how about if
> we limit the number of pages to pin with rlimit ? The rlimit check can
> also include the guest RAM pinning.
rlimit sounds like a sensible thing to do. It would be interesting to
hear what the general policy is wrt guest sizes that KVM folk do ...
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:54PM -0500, Brijesh Singh wrote:
> The command is used to retrieve the measurement of memory encrypted
> through the LAUNCH_UPDATE_DATA command. This measurement can be used
> for attestation purposes.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 52 insertions(+)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 91b070f..9b672eb 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -5957,6 +5957,54 @@ static int sev_launch_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return ret;
> }
>
> +static int sev_launch_measure(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + struct sev_data_launch_measure *data = NULL;
> + struct kvm_sev_launch_measure params;
> + void *addr = NULL;
> + int ret;
> +
> + if (!sev_guest(kvm))
> + return -ENOTTY;
> +
> + if (copy_from_user(¶ms, (void *)argp->data,
> + sizeof(struct kvm_sev_launch_measure)))
> + return -EFAULT;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + if (params.address && params.length) {
Same notes as before: check userspace data and make data a stack var.
Thx.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:55PM -0500, Brijesh Singh wrote:
> The command is used for finializing the SEV guest launch process.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 23 +++++++++++++++++++++++
> 1 file changed, 23 insertions(+)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 9b672eb..7a77197 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -6005,6 +6005,25 @@ static int sev_launch_measure(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return ret;
> }
>
> +static int sev_launch_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + struct sev_data_launch_finish *data;
This one is a very good example why you should do this on the stack:
this struct has a single u32 handle; member. The amount of operations to
do kzalloc and kfree for a single u32 are simply not justified.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:56PM -0500, Brijesh Singh wrote:
> The command is used for querying the SEV guest status.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 38 ++++++++++++++++++++++++++++++++++++++
> 1 file changed, 38 insertions(+)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 7a77197..21f85e1 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -6024,6 +6024,40 @@ static int sev_launch_finish(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return ret;
> }
>
> +static int sev_guest_status(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + struct kvm_sev_guest_status params;
> + struct sev_data_guest_status *data;
> + int ret;
> +
> + if (!sev_guest(kvm))
> + return -ENOTTY;
> +
> + if (copy_from_user(¶ms, (void *) argp->data,
> + sizeof(struct kvm_sev_guest_status)))
Let me try to understand what's going on here. You copy user data into
params...
> + return -EFAULT;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->handle = sev_get_handle(kvm);
> + ret = sev_issue_cmd(kvm, SEV_CMD_GUEST_STATUS, data, &argp->error);
> + if (ret)
> + goto e_free;
> +
> + params.policy = data->policy;
> + params.state = data->state;
> + params.handle = data->handle;
... *overwrite* the copied data which means, the copy meant *absolutely*
*nothing* at all! ...
Also, why does userspace need to know the firmware ->handle?
> +
> + if (copy_to_user((void *) argp->data, ¶ms,
> + sizeof(struct kvm_sev_guest_status)))
... and here you copy it back. And the caller svm_memory_encryption_op()
copies sev_cmd yet again! Probably for the sev_cmd.error value.
Ok, looking at other commands, they use more members like fd in
sev_guest_init(), for example.
But please audit all that shuffling of data back and forth and bring it
down to a mininum. No useless work pls.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:57PM -0500, Brijesh Singh wrote:
> The command is used for decrypting a guest memory region for debug
> purposes.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 160 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 160 insertions(+)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 21f85e1..933384a 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -6058,6 +6058,162 @@ static int sev_guest_status(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return ret;
> }
>
> +static int __sev_dbg_enc_dec(struct kvm *kvm, unsigned long src,
> + unsigned long dst, int size, int *error, bool enc)
> +{
> + struct sev_data_dbg *data;
on stack
> + int ret;
> +
> + data = kzalloc(sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> +
> + data->handle = sev_get_handle(kvm);
> + data->dst_addr = dst;
> + data->src_addr = src;
> + data->length = size;
> +
> + ret = sev_issue_cmd(kvm,
> + enc ? SEV_CMD_DBG_ENCRYPT : SEV_CMD_DBG_DECRYPT,
> + data, error);
> + kfree(data);
> + return ret;
> +}
> +
> +/*
> + * Decrypt source memory into userspace or kernel buffer. If destination buffer
> + * or len is not aligned to 16-byte boundary then it uses intermediate buffer.
> + */
> +static int __sev_dbg_dec(struct kvm *kvm, unsigned long paddr,
> + unsigned long __user dst_uaddr,
> + unsigned long dst_kaddr, unsigned long dst_paddr,
> + int size, int *error)
> +{
> + int ret, offset, len = size;
> + struct page *tpage = NULL;
> +
> + /*
> + * Debug command works with 16-byte aligned inputs, check if all inputs
> + * (src, dst and len) are 16-byte aligned. If one of the input is not
> + * aligned then we decrypt more than requested into a temporary buffer
> + * and copy the porition of data into destination buffer.
> + */
> + if (!IS_ALIGNED(paddr, 16) || !IS_ALIGNED(dst_paddr, 16) ||
> + !IS_ALIGNED(size, 16)) {
Align vertically for better readability:
if (!IS_ALIGNED(paddr, 16) ||
!IS_ALIGNED(dst_paddr, 16) ||
!IS_ALIGNED(size, 16)) {
Also, I was going to suggest to simplify that test by OR-ing them all
but gcc does that already for ya:
.loc 1 6107 0
movq %r12, %rax # _584, tmp534
orq %rbx, %rax # _592, tmp534
testb $15, %al #, tmp534
je .L2582 #,
> + tpage = (void *)alloc_page(GFP_KERNEL);
> + if (!tpage)
> + return -ENOMEM;
> +
> + dst_paddr = __sme_page_pa(tpage);
> +
> + /*
> + * if source buffer is not aligned then offset will be used
> + * when copying the data from the temporary buffer into
> + * destination buffer.
> + */
> + offset = paddr & 15;
> +
> + /* its safe to read more than requested size. */
> + len = round_up(size + offset, 16);
> +
> + paddr = round_down(paddr, 16);
> + }
> +
> + ret = __sev_dbg_enc_dec(kvm, paddr, dst_paddr, len, error, false);
Call that function what it does: __sev_issue_dbg_cmd() or so. It is hard
to keep its name apart from the enclosing __sev_dbg_dec().
> + /*
> + * If temporary buffer is used then copy the data from temporary buffer
> + * into destination buffer.
> + */
> + if (tpage) {
> +
> + /*
> + * If destination buffer is a userspace buffer then use
> + * copy_to_user otherwise memcpy.
> + */
> + if (dst_uaddr) {
> + if (copy_to_user((uint8_t *)dst_uaddr,
> + page_address(tpage) + offset, size))
> + ret = -EFAULT;
Align arguments on the opening brace.
> + } else {
> + memcpy((void *)dst_kaddr,
> + page_address(tpage) + offset, size);
Let it stick out.
> + }
> +
> + __free_page(tpage);
> + }
> +
> + return ret;
> +}
> +
> +static int sev_dbg_decrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
> + unsigned long vaddr, vaddr_end, next_vaddr;
> + unsigned long dst_vaddr, dst_vaddr_end;
> + struct page **srcpage, **dstpage;
> + struct kvm_sev_dbg debug;
> + unsigned long n;
> + int ret, size;
> +
> + if (!sev_guest(kvm))
> + return -ENOTTY;
> +
> + if (copy_from_user(&debug, (void *)argp->data,
> + sizeof(struct kvm_sev_dbg)))
> + return -EFAULT;
> +
> + vaddr = debug.src_addr;
> + size = debug.length;
> + vaddr_end = vaddr + size;
> + dst_vaddr = debug.dst_addr;
> + dst_vaddr_end = dst_vaddr + size;
This allows poking in any address but I guess you want that for a
debugging use case. As you said, people will be advised to disable the
debugging feature in production systems.
> +
> + for (; vaddr < vaddr_end; vaddr = next_vaddr) {
> + int len, s_off, d_off;
> +
> + /* lock userspace source and destination page */
> + srcpage = sev_pin_memory(vaddr & PAGE_MASK, PAGE_SIZE, &n, 0);
> + if (!srcpage)
> + return -EFAULT;
> +
> + dstpage = sev_pin_memory(dst_vaddr & PAGE_MASK, PAGE_SIZE,
> + &n, 1);
Let it stick out or call those src_p and dst_p.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On 9/14/17 5:35 AM, Borislav Petkov wrote:
...
> +
>> + if (copy_from_user(¶ms, (void *) argp->data,
>> + sizeof(struct kvm_sev_guest_status)))
> Let me try to understand what's going on here. You copy user data into
> params...
This is wrong -- since all the parameters in GET_STATUS is "OUT" hence
we don't need to perform copy_from_user. I will fix it. thanks
>
>> + return -EFAULT;
>> +
>> + data = kzalloc(sizeof(*data), GFP_KERNEL);
>> + if (!data)
>> + return -ENOMEM;
>> +
>> + data->handle = sev_get_handle(kvm);
>> + ret = sev_issue_cmd(kvm, SEV_CMD_GUEST_STATUS, data, &argp->error);
>> + if (ret)
>> + goto e_free;
>> +
>> + params.policy = data->policy;
>> + params.state = data->state;
>> + params.handle = data->handle;
> ... *overwrite* the copied data which means, the copy meant *absolutely*
> *nothing* at all! ...
>
> Also, why does userspace need to know the firmware ->handle?
SEV firmware supports key-sharing, if guest policy allows sharing the
key between VMs then we need the firmware->handle. If key-sharing
feature is used then firmware->handle of the 1st VM will be passed into
the LAUNCH_START of 2nd VM. I still have not coded up anything in qemu
for key-sharing and also I am using GET_STATUS command in qemu yet. But
wanted to make sure that if we decide to add "info sev-status" command
in qemu-monitor to retrieve the SEV state information and then all the
information is available to us.
On Mon, Jul 24, 2017 at 03:02:58PM -0500, Brijesh Singh wrote:
> The command copies a plain text into guest memory and encrypts it using
> the VM encryption key. The command will be used for debug purposes
> (e.g setting breakpoint through gdbserver)
"setting a breakpoint" or "setting breakpoints"
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 174 insertions(+)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 933384a..75dcaa9 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -6214,6 +6214,176 @@ static int sev_dbg_decrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
> return ret;
> }
>
> +static int __sev_dbg_enc(struct kvm *kvm, unsigned long __user vaddr,
__sev_dbg_encrypt
> + unsigned long paddr, unsigned long __user dst_vaddr,
> + unsigned long dst_paddr, int size, int *error)
> +{
> + struct page *src_tpage = NULL;
> + struct page *dst_tpage = NULL;
> + int ret, len = size;
> +
> + /*
> + * Debug encrypt command works with 16-byte aligned inputs. Function
> + * handles the alingment issue as below:
> + *
> + * case 1
> + * If source buffer is not 16-byte aligned then we copy the data from
> + * source buffer into a PAGE aligned intermediate (src_tpage) buffer
"page-aligned"
> + * and use this intermediate buffer as source buffer
> + *
> + * case 2
> + * If destination buffer or length is not 16-byte aligned then:
> + * - decrypt portion of destination buffer into intermediate buffer
> + * (dst_tpage)
> + * - copy the source data into intermediate buffer
> + * - use the intermediate buffer as source buffer
> + */
> +
> + /* If source is not aligned (case 1) */
So put the whole case 1 comment here directly - no need to reference it
again. Ditto for case 2 below.
> + if (!IS_ALIGNED(vaddr, 16)) {
> + src_tpage = alloc_page(GFP_KERNEL);
> + if (!src_tpage)
> + return -ENOMEM;
> +
> + if (copy_from_user(page_address(src_tpage),
> + (uint8_t *)vaddr, size)) {
Let it stick out.
> + __free_page(src_tpage);
> + return -EFAULT;
> + }
> + paddr = __sme_page_pa(src_tpage);
> +
> + /* flush the caches to ensure that DRAM has recent contents */
That comment needs improving.
> + clflush_cache_range(page_address(src_tpage), PAGE_SIZE);
> + }
> +
> + /* If destination buffer or length is not aligned (case 2) */
> + if (!IS_ALIGNED(dst_vaddr, 16) || !IS_ALIGNED(size, 16)) {
> + int dst_offset;
> +
> + dst_tpage = alloc_page(GFP_KERNEL);
> + if (!dst_tpage) {
> + ret = -ENOMEM;
> + goto e_free;
> + }
> +
> + /* decrypt destination buffer into intermediate buffer */
> + ret = __sev_dbg_dec(kvm,
> + round_down(dst_paddr, 16),
> + 0,
> + (unsigned long)page_address(dst_tpage),
> + __sme_page_pa(dst_tpage),
> + round_up(size, 16),
> + error);
> + if (ret)
> + goto e_free;
> +
> + dst_offset = dst_paddr & 15;
> +
> + /*
> + * modify the intermediate buffer with data from source
> + * buffer.
> + */
> + if (src_tpage)
> + memcpy(page_address(dst_tpage) + dst_offset,
> + page_address(src_tpage), size);
> + else {
> + if (copy_from_user(page_address(dst_tpage) + dst_offset,
> + (void *) vaddr, size)) {
Let it stick out.
> + ret = -EFAULT;
> + goto e_free;
> + }
> + }
> +
> +
> + /* use intermediate buffer as source */
> + paddr = __sme_page_pa(dst_tpage);
> +
> + /* flush the caches to ensure that DRAM gets recent updates */
Comment needs improvement.
> + clflush_cache_range(page_address(dst_tpage), PAGE_SIZE);
> +
> + /* now we have length and destination buffer aligned */
> + dst_paddr = round_down(dst_paddr, 16);
> + len = round_up(size, 16);
> + }
> +
> + ret = __sev_dbg_enc_dec(kvm, paddr, dst_paddr, len, error, true);
<---- newline here.
> +e_free:
> + if (src_tpage)
> + __free_page(src_tpage);
> + if (dst_tpage)
> + __free_page(dst_tpage);
> + return ret;
> +}
> +
> +static int sev_dbg_encrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
> +{
This function is almost an exact duplication of sev_dbg_decrypt(). Add
a __sev_dbg_crypt() workhorse function which does it all and has callers
sev_dbg_decrypt() and sev_dbg_encrypt().
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:02:59PM -0500, Brijesh Singh wrote:
> The SEV memory encryption engine uses a tweak such that two identical
> plaintexts at different location will have a different ciphertexts.
plaintexts or plaintext pages? also, s/a //
> So swapping or moving ciphertexts of two pages will not result in
> plaintexts being swapped. Relocating (or migrating) a physical backing
s/a //
> pages for SEV guest will require some additional steps. The current SEV
"for a SEV guest"
> key management spec does not provide commands to swap or migrate (move)
> ciphertexts. For now, we pin the guest memory registered through
> KVM_MEMORY_ENCRYPT_REGISTER_RAM ioctl.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/include/asm/kvm_host.h | 1 +
> arch/x86/kvm/svm.c | 113 ++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 114 insertions(+)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index 150177e..a91aadf 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -747,6 +747,7 @@ struct kvm_sev_info {
> unsigned int handle; /* firmware handle */
> unsigned int asid; /* asid for this guest */
> int sev_fd; /* SEV device fd */
> + struct list_head ram_list; /* list of registered ram */
regions_list I guess.
> };
>
> struct kvm_arch {
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 75dcaa9..cdb1cf3 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -333,8 +333,19 @@ static int sev_asid_new(void);
> static void sev_asid_free(int asid);
> static void sev_deactivate_handle(struct kvm *kvm, int *error);
> static void sev_decommission_handle(struct kvm *kvm, int *error);
> +static void sev_unpin_memory(struct page **pages, unsigned long npages);
Unneeded.
> +
> #define __sme_page_pa(x) __sme_set(page_to_pfn(x) << PAGE_SHIFT)
>
> +struct kvm_sev_pin_ram {
sev_pinned_region
> + struct list_head list;
> + unsigned long npages;
> + struct page **pages;
> + struct kvm_memory_encrypt_ram userspace;
That member would need a comment what it is.
> +};
> +
> +static void __mem_encrypt_unregister_ram(struct kvm_sev_pin_ram *ram);
Move code so that you don't need that one.
> +
> static bool svm_sev_enabled(void)
> {
> return !!max_sev_asid;
> @@ -385,6 +396,11 @@ static inline void sev_set_fd(struct kvm *kvm, int fd)
> to_sev_info(kvm)->sev_fd = fd;
> }
>
> +static inline struct list_head *sev_get_ram_list(struct kvm *kvm)
> +{
> + return &to_sev_info(kvm)->ram_list;
> +}
> +
> static inline void mark_all_dirty(struct vmcb *vmcb)
> {
> vmcb->control.clean = 0;
> @@ -1566,10 +1582,24 @@ static void sev_firmware_uninit(void)
> static void sev_vm_destroy(struct kvm *kvm)
> {
> int state, error;
> + struct list_head *pos, *q;
> + struct kvm_sev_pin_ram *ram;
> + struct list_head *head = sev_get_ram_list(kvm);
Please sort function local variables declaration in a reverse christmas
tree order:
<type> longest_variable_name;
<type> shorter_var_name;
<type> even_shorter;
<type> i;
>
> if (!sev_guest(kvm))
> return;
>
> + /*
> + * if userspace was terminated before unregistering the memory region
> + * then lets unpin all the registered memory.
> + */
> + if (!list_empty(head)) {
> + list_for_each_safe(pos, q, head) {
> + ram = list_entry(pos, struct kvm_sev_pin_ram, list);
> + __mem_encrypt_unregister_ram(ram);
You don't need the local "ram" varible here:
__mem_encrypt_unregister_ram(list_entry(pos, struct kvm_sev_pin_ram, list));
> + }
> + }
> +
> /* release the firmware resources for this guest */
> if (sev_get_handle(kvm)) {
> sev_deactivate_handle(kvm, &error);
> @@ -5640,6 +5670,7 @@ static int sev_guest_init(struct kvm *kvm, struct kvm_sev_cmd *argp)
> sev_set_active(kvm);
> sev_set_asid(kvm, asid);
> sev_set_fd(kvm, argp->sev_fd);
> + INIT_LIST_HEAD(sev_get_ram_list(kvm));
> ret = 0;
> e_err:
> fdput(f);
> @@ -6437,6 +6468,86 @@ static int svm_memory_encryption_op(struct kvm *kvm, void __user *argp)
> return r;
> }
>
> +static int mem_encrypt_register_ram(struct kvm *kvm,
> + struct kvm_memory_encrypt_ram *ram)
> +{
Please call that arg "regions" or so. "ram" is strange. In the other
functions too.
> + struct list_head *head = sev_get_ram_list(kvm);
> + struct kvm_sev_pin_ram *pin_ram;
> +
> + if (!sev_guest(kvm))
> + return -ENOTTY;
> +
> + pin_ram = kzalloc(sizeof(*pin_ram), GFP_KERNEL);
> + if (!pin_ram)
> + return -ENOMEM;
> +
> + pin_ram->pages = sev_pin_memory(ram->address, ram->size,
> + &pin_ram->npages, 1);
Let it stick out.
> + if (!pin_ram->pages)
> + goto e_free;
> +
> + /*
> + * Guest may change the memory encryption attribute from C=0 -> C=1
"The guest"
> + * for this memory range. Lets make sure caches are flushed to ensure
> + * that guest data gets written into memory with correct C-bit.
> + */
> + sev_clflush_pages(pin_ram->pages, pin_ram->npages);
> +
> + pin_ram->userspace.address = ram->address;
> + pin_ram->userspace.size = ram->size;
> + list_add_tail(&pin_ram->list, head);
> + return 0;
<---- newline here.
> +e_free:
> + kfree(pin_ram);
> + return 1;
> +}
> +
> +static struct kvm_sev_pin_ram *sev_find_pinned_ram(struct kvm *kvm,
> + struct kvm_memory_encrypt_ram *ram)
So this function signature is almost impossible to read: you have "kvm"
"sev" "pin" "ram" and those long structure names.
Now look how something like this:
static struct regions_list *
sev_find_pinned_memory(struct kvm *kvm, struct enc_range *range)
tells you exactly what the function does.
> +{
> + struct list_head *head = sev_get_ram_list(kvm);
> + struct kvm_sev_pin_ram *i;
> +
> + list_for_each_entry(i, head, list) {
> + if (i->userspace.address == ram->address &&
> + i->userspace.size == ram->size)
if (i->usr.addr == reg->addr &&
i->usr.size == reg->size)
reads much better to me.
> + return i;
> + }
> +
> + return NULL;
> +}
> +
> +static void __mem_encrypt_unregister_ram(struct kvm_sev_pin_ram *ram)
> +{
> + /*
> + * Guest may have changed the memory encryption attribute from
"The guest"
> + * C=0 -> C=1. Lets make sure caches are flushed to ensure in data
Both comments talk about the 0 -> 1 case for the C-bit. What about the
reverse: 1->0? Do we not flush there or we don't have cases where a
guest doesn't decrypt its memory?
> + * gets written into memory with correct C-bit.
> + */
> + sev_clflush_pages(ram->pages, ram->npages);
> +
> + sev_unpin_memory(ram->pages, ram->npages);
> + list_del(&ram->list);
> + kfree(ram);
> +}
> +
> +static int mem_encrypt_unregister_ram(struct kvm *kvm,
> + struct kvm_memory_encrypt_ram *ram)
> +{
> + struct kvm_sev_pin_ram *pinned_ram;
> +
> + if (!sev_guest(kvm))
> + return -ENOTTY;
> +
> + pinned_ram = sev_find_pinned_ram(kvm, ram);
> + if (!pinned_ram)
> + return -EINVAL;
> +
> + __mem_encrypt_unregister_ram(pinned_ram);
> +
> + return 0;
> +}
> +
> static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
> .cpu_has_kvm_support = has_svm,
> .disabled_by_bios = is_disabled,
> @@ -6551,6 +6662,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
> .setup_mce = svm_setup_mce,
>
> .memory_encryption_op = svm_memory_encryption_op,
> + .memory_encryption_register_ram = mem_encrypt_register_ram,
> + .memory_encryption_unregister_ram = mem_encrypt_unregister_ram,
Names are too long. mem_encrypt_reg_memory or so I guess. In general,
choose a prefix and stick with it. mem_enc, mem_encrypt, mem_crypt...
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:03:00PM -0500, Brijesh Singh wrote:
> Extend kvm_x86_ops to add memory_encyption_enabled() ops.
> It returns a
> boolean indicating whether memory encryption is enabled on the VCPU.
You don't need that sentence.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/include/asm/kvm_host.h | 1 +
> arch/x86/kvm/svm.c | 8 ++++++++
> 2 files changed, 9 insertions(+)
>
> diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
> index a91aadf..a14d4dd 100644
> --- a/arch/x86/include/asm/kvm_host.h
> +++ b/arch/x86/include/asm/kvm_host.h
> @@ -1073,6 +1073,7 @@ struct kvm_x86_ops {
> struct kvm_memory_encrypt_ram *ram);
> int (*memory_encryption_unregister_ram)(struct kvm *kvm,
> struct kvm_memory_encrypt_ram *ram);
> + bool (*memory_encryption_enabled)(struct kvm_vcpu *vcpu);
> };
>
> struct kvm_arch_async_pf {
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index cdb1cf3..0bbd050 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -6548,6 +6548,12 @@ static int mem_encrypt_unregister_ram(struct kvm *kvm,
> return 0;
> }
>
> +static bool mem_encrypt_enabled(struct kvm_vcpu *vcpu)
> +{
> + return !!(to_svm(vcpu)->vmcb->control.nested_ctl &
> + SVM_NESTED_CTL_SEV_ENABLE);
You don't need the !!
> +}
> +
> static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
> .cpu_has_kvm_support = has_svm,
> .disabled_by_bios = is_disabled,
> @@ -6664,6 +6670,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
> .memory_encryption_op = svm_memory_encryption_op,
> .memory_encryption_register_ram = mem_encrypt_register_ram,
> .memory_encryption_unregister_ram = mem_encrypt_unregister_ram,
> + .memory_encryption_enabled = mem_encrypt_enabled,
> +
mem_enc_enabled looks better to me.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:03:01PM -0500, Brijesh Singh wrote:
> When SEV is active, on #NPF the page fault address will contain C-bit.
"... contain the C-bit."
> We must clear the C-bit before handling the fault.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 0bbd050..64b9f60 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -2321,7 +2321,7 @@ static void svm_set_dr7(struct kvm_vcpu *vcpu, unsigned long value)
>
> static int pf_interception(struct vcpu_svm *svm)
> {
> - u64 fault_address = svm->vmcb->control.exit_info_2;
> + u64 fault_address = __sme_clr(svm->vmcb->control.exit_info_2);
> u64 error_code = svm->vmcb->control.exit_info_1;
>
> return kvm_handle_page_fault(&svm->vcpu, error_code, fault_address,
> --
Otherwise:
Reviewed-by: Borislav Petkov <[email protected]>
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:03:02PM -0500, Brijesh Singh wrote:
> On #UD, x86_emulate_instruction() fetches the data from guest memory and
> decodes the instruction bytes to assist further. When SEV is enabled, the
> instruction bytes will be encrypted using the guest-specific key, hypervisor
"... key and the hypervisor... "
> will no longer able to fetch the instruction bytes to assist UD handling.
> By not installing intercept we let the guest receive and handle #UD.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/svm.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
> index 64b9f60..4581d03 100644
> --- a/arch/x86/kvm/svm.c
> +++ b/arch/x86/kvm/svm.c
> @@ -1432,8 +1432,10 @@ static void init_vmcb(struct vcpu_svm *svm)
> svm->vmcb->control.virt_ext |= VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK;
> }
>
> - if (sev_guest(svm->vcpu.kvm))
> + if (sev_guest(svm->vcpu.kvm)) {
> svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE;
> + clr_exception_intercept(svm, UD_VECTOR);
> + }
>
> mark_all_dirty(svm->vmcb);
>
> --
Otherwise:
Reviewed-by: Borislav Petkov <[email protected]>
Btw, if this is really important for the hypervisor to continue to be
able to do decode assist, we probably should think about having the
guest give the hypervisor the couple instruction bytes in a controlled
manner...
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--
On Mon, Jul 24, 2017 at 03:03:03PM -0500, Brijesh Singh wrote:
> On AMD platform, under certain conditions insn_len may be zero on #NPF.
"On AMD platforms... "
> This can happen if guest gets a page-fault on data access, but HW table
" ... if a guest... .. but the HW table walker ..."
> walker is not able to read the instruction page (e.g instuction page
> is not present in memory).
>
> Typically, when insn_len is zero, x86_emulate_instruction() walks the
> guest page table and fetches the instruction bytes from guest memory.
> When SEV is enabled, the guest memory is encrypted with guest-specific
> key hence hypervisor will not able to fetch the instruction bytes.
> In those cases we simply restart the guest.
Same question as in the previous patch: should we make this a proper
interface?
> I have encountered this issue when running kernbench inside the guest.
>
> Signed-off-by: Brijesh Singh <[email protected]>
> ---
> arch/x86/kvm/mmu.c | 17 +++++++++++++++++
> 1 file changed, 17 insertions(+)
>
> diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
> index ccb70b8..be41ad0 100644
> --- a/arch/x86/kvm/mmu.c
> +++ b/arch/x86/kvm/mmu.c
> @@ -4850,6 +4850,23 @@ int kvm_mmu_page_fault(struct kvm_vcpu *vcpu, gva_t cr2, u64 error_code,
> if (mmio_info_in_cache(vcpu, cr2, direct))
> emulation_type = 0;
> emulate:
> + /*
> + * On AMD platform, under certain conditions insn_len may be zero on #NPF.
Same typos as above.
> + * This can happen if guest gets a page-fault on data access, but HW table
> + * walker is not able to read the instruction page (e.g instuction page
> + * is not present).
> + *
> + * Typically, when insn_len is zero, x86_emulate_instruction() walks the
> + * guest page table and fetches the instruction bytes. When SEV is active,
> + * the guest memory is encrypted with guest key hence we will not able to
> + * fetch the instruction bytes. In those cases we simply restart the guest.
> + */
> + if (unlikely(!insn_len)) {
> + if (kvm_x86_ops->memory_encryption_enabled &&
> + kvm_x86_ops->memory_encryption_enabled(vcpu))
Align vertically.
--
Regards/Gruss,
Boris.
SUSE Linux GmbH, GF: Felix Imendörffer, Jane Smithard, Graham Norton, HRB 21284 (AG Nürnberg)
--