* [RFC PATCH v2 13/32] KVM: SVM: Enable SEV by setting the SEV_ENABLE CPU feature
From: Brijesh Singh @ 2017-03-02 15:15 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
Modify the SVM cpuid update function to indicate if Secure Encrypted
Virtualization (SEV) is active in the guest by setting the SEV KVM CPU
features bit. SEV is active if Secure Memory Encryption is enabled in
the host and the SEV_ENABLE bit of the VMCB is set.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
arch/x86/kvm/cpuid.c | 4 +++-
arch/x86/kvm/svm.c | 18 ++++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c
index 1639de8..e0c40a8 100644
--- a/arch/x86/kvm/cpuid.c
+++ b/arch/x86/kvm/cpuid.c
@@ -601,7 +601,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;
@@ -634,6 +634,8 @@ static inline int __do_cpuid_ent(struct kvm_cpuid_entry2 *entry, u32 function,
break;
case 0x8000001d:
break;
+ case 0x8000001f:
+ break;
/*Add support for Centaur's CPUID instruction*/
case 0xC0000000:
/*Just support up to 0xC0000004 now*/
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 75b0645..36d61ff 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -46,6 +46,7 @@
#include <asm/irq_remapping.h>
#include <asm/virtext.h>
+#include <asm/mem_encrypt.h>
#include "trace.h"
#define __ex(x) __kvm_handle_fault_on_reboot(x)
@@ -5005,10 +5006,27 @@ static void svm_cpuid_update(struct kvm_vcpu *vcpu)
{
struct vcpu_svm *svm = to_svm(vcpu);
struct kvm_cpuid_entry2 *entry;
+ struct vmcb_control_area *ca = &svm->vmcb->control;
+ struct kvm_cpuid_entry2 *features, *sev_info;
/* Update nrips enabled cache */
svm->nrips_enabled = !!guest_cpuid_has_nrips(&svm->vcpu);
+ /* Check for Secure Encrypted Virtualization support */
+ features = kvm_find_cpuid_entry(vcpu, KVM_CPUID_FEATURES, 0);
+ if (!features)
+ return;
+
+ sev_info = kvm_find_cpuid_entry(vcpu, 0x8000001f, 0);
+ if (!sev_info)
+ return;
+
+ if (ca->nested_ctl & SVM_NESTED_CTL_SEV_ENABLE) {
+ features->eax |= (1 << KVM_FEATURE_SEV);
+ cpuid(0x8000001f, &sev_info->eax, &sev_info->ebx,
+ &sev_info->ecx, &sev_info->edx);
+ }
+
if (!kvm_vcpu_apicv_active(vcpu))
return;
^ permalink raw reply related
* Re: Problem with RSA test from testmgr
From: Tadeusz Struk @ 2017-03-02 16:30 UTC (permalink / raw)
To: Corentin Labbe, Stephan Müller; +Cc: herbert, linux-crypto, linux-kernel
In-Reply-To: <20170302062158.GA9242@Red>
On 03/01/2017 10:21 PM, Corentin Labbe wrote:
> I am finishing a patch that made testmgr test both (padded and unpadded).
Even if you patch the test vectors there is no guarantee that a user
of the API will always have the plain text padded.
It can be anything between 1 and the key size.
This needs to be the driver who adds padding if needed.
See how other implementations handle it.
Thanks,
--
Tadeusz
^ permalink raw reply
* Re: Problem with RSA test from testmgr
From: Tadeusz Struk @ 2017-03-02 16:26 UTC (permalink / raw)
To: Stephan Müller; +Cc: Corentin Labbe, herbert, linux-crypto, linux-kernel
In-Reply-To: <3589679.E0B746otbi@tauon.atsec.com>
Hi Stephan,
On 03/01/2017 10:08 PM, Stephan Müller wrote:
>> memset(ptextp, 0, 256);
>> memcpy(ptextp + 64 - 8, ptext_ex, plen);
> I actually have tested that and it did not return the data the kernel
> implementation would return
It did for me:
Result 64 plen=8
63 1c cd 7b e1 7e e4 de c9 a8 89 a1 74 cb 3c 63 7d 24 ec 83 c3 15 e4 7f 73 05 34 d1 ec 22 bb 8a 5e 32 39 6d c1 1d 7d 50 3b 9f 7a ad f0 2e 25 53 9f 6e bd 4c 55 84 0c 9b cf 1a 4b 51 1e 9e 0c 06
Are you sure you are compering this with the fist test vector?
http://lxr.free-electrons.com/source/crypto/testmgr.h#L183
Thanks,
--
Tadeusz
^ permalink raw reply
* [RFC PATCH v2 16/32] x86: kvm: Provide support to create Guest and HV shared per-CPU variables
From: Brijesh Singh @ 2017-03-02 15:15 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
Some KVM specific MSR's (steal-time, asyncpf, avic_eio) allocates per-CPU
variable at compile time and share its physical address with hypervisor.
It presents a challege when SEV is active in guest OS. When SEV is active,
guest memory is encrypted with guest key and hypervisor will no longer able
to modify the guest memory. When SEV is active, we need to clear the
encryption attribute of shared physical addresses so that both guest and
hypervisor can access the data.
To solve this problem, I have tried these three options:
1) Convert the static per-CPU to dynamic per-CPU allocation. When SEV is
detected then clear the encryption attribute. But while doing so I found
that per-CPU dynamic allocator was not ready when kvm_guest_cpu_init was
called.
2) Since the encryption attributes works on PAGE_SIZE hence add some extra
padding to 'struct kvm-steal-time' to make it PAGE_SIZE and then at runtime
clear the encryption attribute of the full PAGE. The downside of this was
now we need to modify structure which may break the compatibility.
3) Define a new per-CPU section (.data..percpu.hv_shared) which will be
used to hold the compile time shared per-CPU variables. When SEV is
detected we map this section with encryption attribute cleared.
This patch implements #3. It introduces a new DEFINE_PER_CPU_HV_SHAHRED
macro to create a compile time per-CPU variable. When SEV is detected we
map the per-CPU variable as decrypted (i.e with encryption attribute cleared).
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
arch/x86/kernel/kvm.c | 43 +++++++++++++++++++++++++++++++------
include/asm-generic/vmlinux.lds.h | 3 +++
include/linux/percpu-defs.h | 9 ++++++++
3 files changed, 48 insertions(+), 7 deletions(-)
diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c
index 099fcba..706a08e 100644
--- a/arch/x86/kernel/kvm.c
+++ b/arch/x86/kernel/kvm.c
@@ -75,8 +75,8 @@ static int parse_no_kvmclock_vsyscall(char *arg)
early_param("no-kvmclock-vsyscall", parse_no_kvmclock_vsyscall);
-static DEFINE_PER_CPU(struct kvm_vcpu_pv_apf_data, apf_reason) __aligned(64);
-static DEFINE_PER_CPU(struct kvm_steal_time, steal_time) __aligned(64);
+static DEFINE_PER_CPU_HV_SHARED(struct kvm_vcpu_pv_apf_data, apf_reason) __aligned(64);
+static DEFINE_PER_CPU_HV_SHARED(struct kvm_steal_time, steal_time) __aligned(64);
static int has_steal_clock = 0;
/*
@@ -290,6 +290,22 @@ static void __init paravirt_ops_setup(void)
#endif
}
+static int kvm_map_percpu_hv_shared(void *addr, unsigned long size)
+{
+ /* When SEV is active, the percpu static variables initialized
+ * in data section will contain the encrypted data so we first
+ * need to decrypt it and then map it as decrypted.
+ */
+ if (sev_active()) {
+ unsigned long pa = slow_virt_to_phys(addr);
+
+ sme_early_decrypt(pa, size);
+ return early_set_memory_decrypted(addr, size);
+ }
+
+ return 0;
+}
+
static void kvm_register_steal_time(void)
{
int cpu = smp_processor_id();
@@ -298,12 +314,17 @@ static void kvm_register_steal_time(void)
if (!has_steal_clock)
return;
+ if (kvm_map_percpu_hv_shared(st, sizeof(*st))) {
+ pr_err("kvm-stealtime: failed to map hv_shared percpu\n");
+ return;
+ }
+
wrmsrl(MSR_KVM_STEAL_TIME, (slow_virt_to_phys(st) | KVM_MSR_ENABLED));
pr_info("kvm-stealtime: cpu %d, msr %llx\n",
cpu, (unsigned long long) slow_virt_to_phys(st));
}
-static DEFINE_PER_CPU(unsigned long, kvm_apic_eoi) = KVM_PV_EOI_DISABLED;
+static DEFINE_PER_CPU_HV_SHARED(unsigned long, kvm_apic_eoi) = KVM_PV_EOI_DISABLED;
static notrace void kvm_guest_apic_eoi_write(u32 reg, u32 val)
{
@@ -327,25 +348,33 @@ static void kvm_guest_cpu_init(void)
if (kvm_para_has_feature(KVM_FEATURE_ASYNC_PF) && kvmapf) {
u64 pa = slow_virt_to_phys(this_cpu_ptr(&apf_reason));
+ if (kvm_map_percpu_hv_shared(this_cpu_ptr(&apf_reason),
+ sizeof(struct kvm_vcpu_pv_apf_data)))
+ goto skip_asyncpf;
#ifdef CONFIG_PREEMPT
pa |= KVM_ASYNC_PF_SEND_ALWAYS;
#endif
wrmsrl(MSR_KVM_ASYNC_PF_EN, pa | KVM_ASYNC_PF_ENABLED);
__this_cpu_write(apf_reason.enabled, 1);
- printk(KERN_INFO"KVM setup async PF for cpu %d\n",
- smp_processor_id());
+ printk(KERN_INFO"KVM setup async PF for cpu %d msr %llx\n",
+ smp_processor_id(), pa);
}
-
+skip_asyncpf:
if (kvm_para_has_feature(KVM_FEATURE_PV_EOI)) {
unsigned long pa;
/* Size alignment is implied but just to make it explicit. */
BUILD_BUG_ON(__alignof__(kvm_apic_eoi) < 4);
+ if (kvm_map_percpu_hv_shared(this_cpu_ptr(&kvm_apic_eoi),
+ sizeof(unsigned long)))
+ goto skip_pv_eoi;
__this_cpu_write(kvm_apic_eoi, 0);
pa = slow_virt_to_phys(this_cpu_ptr(&kvm_apic_eoi))
| KVM_MSR_ENABLED;
wrmsrl(MSR_KVM_PV_EOI_EN, pa);
+ printk(KERN_INFO"KVM setup PV EOI for cpu %d msr %lx\n",
+ smp_processor_id(), pa);
}
-
+skip_pv_eoi:
if (has_steal_clock)
kvm_register_steal_time();
}
diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h
index 0968d13..8d29910 100644
--- a/include/asm-generic/vmlinux.lds.h
+++ b/include/asm-generic/vmlinux.lds.h
@@ -773,6 +773,9 @@
. = ALIGN(cacheline); \
*(.data..percpu) \
*(.data..percpu..shared_aligned) \
+ . = ALIGN(PAGE_SIZE); \
+ *(.data..percpu..hv_shared) \
+ . = ALIGN(PAGE_SIZE); \
VMLINUX_SYMBOL(__per_cpu_end) = .;
/**
diff --git a/include/linux/percpu-defs.h b/include/linux/percpu-defs.h
index 8f16299..5af366e 100644
--- a/include/linux/percpu-defs.h
+++ b/include/linux/percpu-defs.h
@@ -172,6 +172,15 @@
#define DEFINE_PER_CPU_READ_MOSTLY(type, name) \
DEFINE_PER_CPU_SECTION(type, name, "..read_mostly")
+/* Declaration/definition used for per-CPU variables that must be shared
+ * between hypervisor and guest OS.
+ */
+#define DECLARE_PER_CPU_HV_SHARED(type, name) \
+ DECLARE_PER_CPU_SECTION(type, name, "..hv_shared")
+
+#define DEFINE_PER_CPU_HV_SHARED(type, name) \
+ DEFINE_PER_CPU_SECTION(type, name, "..hv_shared")
+
/*
* Intermodule exports for per-CPU variables. sparse forgets about
* address space across EXPORT_SYMBOL(), change EXPORT_SYMBOL() to
^ permalink raw reply related
* [RFC PATCH v2 24/32] kvm: x86: prepare for SEV guest management API support
From: Brijesh Singh @ 2017-03-02 15:17 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
The patch adds initial support required to integrate Secure Encrypted
Virtualization (SEV) feature.
ASID management:
- Reserve asid range for SEV guest, SEV asid range is obtained through
CPUID Fn8000_001f[ECX]. A non-SEV guest can use any asid outside the SEV
asid range.
- SEV guest must have asid value within asid range obtained through CPUID.
- SEV guest must have the same asid for all vcpu's. A TLB flush is required
if different vcpu for the same ASID is to be run on the same host CPU.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
arch/x86/include/asm/kvm_host.h | 8 ++
arch/x86/kvm/svm.c | 189 +++++++++++++++++++++++++++++++++++++++
include/uapi/linux/kvm.h | 98 ++++++++++++++++++++
3 files changed, 294 insertions(+), 1 deletion(-)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 62651ad..fcc4710 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -719,6 +719,12 @@ struct kvm_hv {
HV_REFERENCE_TSC_PAGE tsc_ref;
};
+struct kvm_sev_info {
+ 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;
@@ -805,6 +811,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 8d8fe62..fb63398 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -36,6 +36,7 @@
#include <linux/slab.h>
#include <linux/amd-iommu.h>
#include <linux/hashtable.h>
+#include <linux/psp-sev.h>
#include <asm/apic.h>
#include <asm/perf_event.h>
@@ -211,6 +212,9 @@ struct vcpu_svm {
*/
struct list_head ir_list;
spinlock_t ir_list_lock;
+
+ /* which host cpu was used for running this vcpu */
+ bool last_cpuid;
};
/*
@@ -490,6 +494,64 @@ static inline bool gif_set(struct vcpu_svm *svm)
return !!(svm->vcpu.arch.hflags & HF_GIF_MASK);
}
+/* Secure Encrypted Virtualization */
+static unsigned int max_sev_asid;
+static unsigned long *sev_asid_bitmap;
+
+static bool kvm_sev_enabled(void)
+{
+ return max_sev_asid ? 1 : 0;
+}
+
+static inline struct kvm_sev_info *sev_get_info(struct kvm *kvm)
+{
+ struct kvm_arch *vm_data = &kvm->arch;
+
+ return &vm_data->sev_info;
+}
+
+static unsigned int sev_get_handle(struct kvm *kvm)
+{
+ struct kvm_sev_info *sev_info = sev_get_info(kvm);
+
+ return sev_info->handle;
+}
+
+static inline int sev_guest(struct kvm *kvm)
+{
+ return sev_get_handle(kvm);
+}
+
+static inline int sev_get_asid(struct kvm *kvm)
+{
+ struct kvm_sev_info *sev_info = sev_get_info(kvm);
+
+ if (!sev_info)
+ return -EINVAL;
+
+ return sev_info->asid;
+}
+
+static inline int sev_get_fd(struct kvm *kvm)
+{
+ struct kvm_sev_info *sev_info = sev_get_info(kvm);
+
+ if (!sev_info)
+ return -EINVAL;
+
+ return sev_info->sev_fd;
+}
+
+static inline void sev_set_asid(struct kvm *kvm, int asid)
+{
+ struct kvm_sev_info *sev_info = sev_get_info(kvm);
+
+ if (!sev_info)
+ return;
+
+ sev_info->asid = asid;
+}
+
static unsigned long iopm_base;
struct kvm_ldttss_desc {
@@ -511,6 +573,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);
@@ -764,7 +828,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;
native_store_gdt(&gdt_descr);
gdt = (struct desc_struct *)gdt_descr.address;
@@ -825,6 +889,7 @@ static void svm_cpu_uninit(int cpu)
per_cpu(svm_data, raw_smp_processor_id()) = NULL;
__free_page(sd->save_area);
+ kfree(sd->sev_vmcbs);
kfree(sd);
}
@@ -842,6 +907,14 @@ static int svm_cpu_init(int cpu)
if (!sd->save_area)
goto err_1;
+ if (kvm_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;
@@ -1017,6 +1090,61 @@ static int avic_ga_log_notifier(u32 ga_tag)
return 0;
}
+static __init void sev_hardware_setup(void)
+{
+ int ret, error, nguests;
+ struct sev_data_init *init;
+ struct sev_data_status *status;
+
+ /*
+ * Get maximum number of encrypted guest supported: Fn8001_001F[ECX]
+ * Bit 31:0: Number of supported guest
+ */
+ nguests = cpuid_ecx(0x8000001F);
+ if (!nguests)
+ return;
+
+ init = kzalloc(sizeof(*init), GFP_KERNEL);
+ if (!init)
+ return;
+
+ status = kzalloc(sizeof(*status), GFP_KERNEL);
+ if (!status)
+ goto err_1;
+
+ /* Initialize SEV firmware */
+ ret = sev_platform_init(init, &error);
+ if (ret) {
+ pr_err("SEV: PLATFORM_INIT ret=%d (%#x)\n", ret, error);
+ goto err_2;
+ }
+
+ /* Initialize SEV ASID bitmap */
+ sev_asid_bitmap = kcalloc(BITS_TO_LONGS(nguests),
+ sizeof(unsigned long), GFP_KERNEL);
+ if (IS_ERR(sev_asid_bitmap)) {
+ sev_platform_shutdown(&error);
+ goto err_2;
+ }
+
+ /* Query the platform status and print API version */
+ ret = sev_platform_status(status, &error);
+ if (ret) {
+ printk(KERN_ERR "SEV: PLATFORM_STATUS ret=%#x\n", error);
+ goto err_2;
+ }
+
+ max_sev_asid = nguests;
+
+ printk(KERN_INFO "kvm: SEV enabled\n");
+ printk(KERN_INFO "SEV API: %d.%d\n",
+ status->api_major, status->api_minor);
+err_2:
+ kfree(status);
+err_1:
+ kfree(init);
+}
+
static __init int svm_hardware_setup(void)
{
int cpu;
@@ -1052,6 +1180,9 @@ static __init int svm_hardware_setup(void)
kvm_enable_efer_bits(EFER_SVME | EFER_LMSLE);
}
+ if (boot_cpu_has(X86_FEATURE_SEV))
+ sev_hardware_setup();
+
for_each_possible_cpu(cpu) {
r = svm_cpu_init(cpu);
if (r)
@@ -1094,10 +1225,25 @@ static __init int svm_hardware_setup(void)
return r;
}
+static __exit void sev_hardware_unsetup(void)
+{
+ int ret, err;
+
+ ret = sev_platform_shutdown(&err);
+ if (ret)
+ printk(KERN_ERR "failed to shutdown PSP rc=%d (%#0x10x)\n",
+ ret, err);
+
+ kfree(sev_asid_bitmap);
+}
+
static __exit void svm_hardware_unsetup(void)
{
int cpu;
+ if (kvm_sev_enabled())
+ sev_hardware_unsetup();
+
for_each_possible_cpu(cpu)
svm_cpu_uninit(cpu);
@@ -1157,6 +1303,11 @@ static void avic_init_vmcb(struct vcpu_svm *svm)
svm->vcpu.arch.apicv_active = true;
}
+static void sev_init_vmcb(struct vcpu_svm *svm)
+{
+ svm->vmcb->control.nested_ctl |= SVM_NESTED_CTL_SEV_ENABLE;
+}
+
static void init_vmcb(struct vcpu_svm *svm)
{
struct vmcb_control_area *control = &svm->vmcb->control;
@@ -1271,6 +1422,9 @@ static void init_vmcb(struct vcpu_svm *svm)
if (avic)
avic_init_vmcb(svm);
+ if (sev_guest(svm->vcpu.kvm))
+ sev_init_vmcb(svm);
+
mark_all_dirty(svm->vmcb);
enable_gif(svm);
@@ -2084,6 +2238,11 @@ static int pf_interception(struct vcpu_svm *svm)
default:
error_code = svm->vmcb->control.exit_info_1;
+ /* In SEV mode, the guest physical address will have C-bit
+ * set. C-bit must be cleared before handling the fault.
+ */
+ if (sev_guest(svm->vcpu.kvm))
+ fault_address &= ~sme_me_mask;
trace_kvm_page_fault(fault_address, error_code);
if (!npt_enabled && kvm_event_needs_reinjection(&svm->vcpu))
kvm_mmu_unprotect_page_virt(&svm->vcpu, fault_address);
@@ -4258,12 +4417,40 @@ static void reload_tss(struct kvm_vcpu *vcpu)
load_TR_desc();
}
+static void pre_sev_run(struct vcpu_svm *svm)
+{
+ int asid = sev_get_asid(svm->vcpu.kvm);
+ int cpu = raw_smp_processor_id();
+ struct svm_cpu_data *sd = per_cpu(svm_data, cpu);
+
+ /* Assign the asid allocated for this SEV guest */
+ svm->vmcb->control.asid = asid;
+
+ /* Flush guest TLB:
+ * - when different VMCB for the same ASID is to be run on the
+ * same host CPU
+ * or
+ * - this VMCB was executed on different host cpu in previous VMRUNs.
+ */
+ if (sd->sev_vmcbs[asid] != (void *)svm->vmcb ||
+ svm->last_cpuid != cpu)
+ svm->vmcb->control.tlb_ctl = TLB_CONTROL_FLUSH_ALL_ASID;
+
+ svm->last_cpuid = cpu;
+ sd->sev_vmcbs[asid] = (void *)svm->vmcb;
+
+ 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);
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index fef7d83..9df37a2 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1284,6 +1284,104 @@ struct kvm_s390_ucas_mapping {
/* Memory Encryption Commands */
#define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xb8, unsigned long)
+/* Secure Encrypted Virtualization mode */
+enum sev_cmd_id {
+ /* Guest launch commands */
+ KVM_SEV_LAUNCH_START = 0,
+ KVM_SEV_LAUNCH_UPDATE_DATA,
+ KVM_SEV_LAUNCH_MEASURE,
+ KVM_SEV_LAUNCH_FINISH,
+ /* Guest migration commands (outgoing) */
+ KVM_SEV_SEND_START,
+ KVM_SEV_SEND_UPDATE_DATA,
+ KVM_SEV_SEND_FINISH,
+ /* Guest migration commands (incoming) */
+ KVM_SEV_RECEIVE_START,
+ KVM_SEV_RECEIVE_UPDATE_DATA,
+ KVM_SEV_RECEIVE_FINISH,
+ /* Guest status and debug commands */
+ KVM_SEV_GUEST_STATUS,
+ KVM_SEV_DBG_DECRYPT,
+ KVM_SEV_DBG_ENCRYPT,
+
+ 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_data;
+ __u32 dh_cert_length;
+ __u64 session_data;
+ __u32 session_length;
+};
+
+struct kvm_sev_launch_update_data {
+ __u64 address;
+ __u32 length;
+};
+
+struct kvm_sev_launch_measure {
+ __u64 address;
+ __u32 length;
+};
+
+struct kvm_sev_send_start {
+ __u64 pdh_cert_data;
+ __u32 pdh_cert_length;
+ __u64 plat_cert_data;
+ __u32 plat_cert_length;
+ __u64 amd_cert_data;
+ __u32 amd_cert_length;
+ __u64 session_data;
+ __u32 session_length;
+};
+
+struct kvm_sev_send_update_data {
+ __u64 hdr_data;
+ __u32 hdr_length;
+ __u64 guest_address;
+ __u32 guest_length;
+ __u64 host_address;
+ __u32 host_length;
+};
+
+struct kvm_sev_receive_start {
+ __u32 handle;
+ __u64 pdh_cert_data;
+ __u32 pdh_cert_length;
+ __u64 session_data;
+ __u32 session_length;
+};
+
+struct kvm_sev_receive_update_data {
+ __u64 hdr_data;
+ __u32 hdr_length;
+ __u64 guest_address;
+ __u32 guest_length;
+ __u64 host_address;
+ __u32 host_length;
+};
+
+struct kvm_sev_guest_status {
+ __u32 handle;
+ __u32 policy;
+ __u32 state;
+};
+
+struct kvm_sev_dbg {
+ __u64 src_addr;
+ __u64 dst_addr;
+ __u32 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)
^ permalink raw reply related
* [RFC PATCH v2 01/32] x86: Add the Secure Encrypted Virtualization CPU feature
From: Brijesh Singh @ 2017-03-02 15:12 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
Update the CPU features to include identifying and reporting on the
Secure Encrypted Virtualization (SEV) feature. SME is identified by
CPUID 0x8000001f, but requires BIOS support to enable it (set bit 23 of
MSR_K8_SYSCFG and set bit 0 of MSR_K7_HWCR). Only show the SEV feature
as available if reported by CPUID and enabled by BIOS.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
arch/x86/include/asm/cpufeatures.h | 1 +
arch/x86/include/asm/msr-index.h | 2 ++
arch/x86/kernel/cpu/amd.c | 22 ++++++++++++++++++----
arch/x86/kernel/cpu/scattered.c | 1 +
4 files changed, 22 insertions(+), 4 deletions(-)
diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h
index b1a4468..9907579 100644
--- a/arch/x86/include/asm/cpufeatures.h
+++ b/arch/x86/include/asm/cpufeatures.h
@@ -188,6 +188,7 @@
*/
#define X86_FEATURE_SME ( 7*32+ 0) /* AMD Secure Memory Encryption */
+#define X86_FEATURE_SEV ( 7*32+ 1) /* AMD Secure Encrypted Virtualization */
#define X86_FEATURE_CPB ( 7*32+ 2) /* AMD Core Performance Boost */
#define X86_FEATURE_EPB ( 7*32+ 3) /* IA32_ENERGY_PERF_BIAS support */
#define X86_FEATURE_CAT_L3 ( 7*32+ 4) /* Cache Allocation Technology L3 */
diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h
index e2d0503..e8b3b28 100644
--- a/arch/x86/include/asm/msr-index.h
+++ b/arch/x86/include/asm/msr-index.h
@@ -361,6 +361,8 @@
#define MSR_K7_PERFCTR3 0xc0010007
#define MSR_K7_CLK_CTL 0xc001001b
#define MSR_K7_HWCR 0xc0010015
+#define MSR_K7_HWCR_SMMLOCK_BIT 0
+#define MSR_K7_HWCR_SMMLOCK BIT_ULL(MSR_K7_HWCR_SMMLOCK_BIT)
#define MSR_K7_FID_VID_CTL 0xc0010041
#define MSR_K7_FID_VID_STATUS 0xc0010042
diff --git a/arch/x86/kernel/cpu/amd.c b/arch/x86/kernel/cpu/amd.c
index 6bddda3..675958e 100644
--- a/arch/x86/kernel/cpu/amd.c
+++ b/arch/x86/kernel/cpu/amd.c
@@ -617,10 +617,13 @@ static void early_init_amd(struct cpuinfo_x86 *c)
set_cpu_bug(c, X86_BUG_AMD_E400);
/*
- * BIOS support is required for SME. If BIOS has enabld SME then
- * adjust x86_phys_bits by the SME physical address space reduction
- * value. If BIOS has not enabled SME then don't advertise the
- * feature (set in scattered.c).
+ * BIOS support is required for SME and SEV.
+ * For SME: If BIOS has enabled SME then adjust x86_phys_bits by
+ * the SME physical address space reduction value.
+ * If BIOS has not enabled SME then don't advertise the
+ * SME feature (set in scattered.c).
+ * For SEV: If BIOS has not enabled SEV then don't advertise the
+ * SEV feature (set in scattered.c).
*/
if (c->extended_cpuid_level >= 0x8000001f) {
if (cpu_has(c, X86_FEATURE_SME)) {
@@ -637,6 +640,17 @@ static void early_init_amd(struct cpuinfo_x86 *c)
clear_cpu_cap(c, X86_FEATURE_SME);
}
}
+
+ if (cpu_has(c, X86_FEATURE_SEV)) {
+ u64 syscfg, hwcr;
+
+ /* Check if SEV is enabled */
+ rdmsrl(MSR_K8_SYSCFG, syscfg);
+ rdmsrl(MSR_K7_HWCR, hwcr);
+ if (!(syscfg & MSR_K8_SYSCFG_MEM_ENCRYPT) ||
+ !(hwcr & MSR_K7_HWCR_SMMLOCK))
+ clear_cpu_cap(c, X86_FEATURE_SEV);
+ }
}
}
diff --git a/arch/x86/kernel/cpu/scattered.c b/arch/x86/kernel/cpu/scattered.c
index cabda87..c3f58d9 100644
--- a/arch/x86/kernel/cpu/scattered.c
+++ b/arch/x86/kernel/cpu/scattered.c
@@ -31,6 +31,7 @@ static const struct cpuid_bit cpuid_bits[] = {
{ X86_FEATURE_CPB, CPUID_EDX, 9, 0x80000007, 0 },
{ X86_FEATURE_PROC_FEEDBACK, CPUID_EDX, 11, 0x80000007, 0 },
{ X86_FEATURE_SME, CPUID_EAX, 0, 0x8000001f, 0 },
+ { X86_FEATURE_SEV, CPUID_EAX, 1, 0x8000001f, 0 },
{ 0, 0, 0, 0, 0 }
};
^ permalink raw reply related
* [RFC PATCH v2 03/32] KVM: SVM: prepare for new bit definition in nested_ctl
From: Brijesh Singh @ 2017-03-02 15:12 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
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 <thomas.lendacky@amd.com>
---
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 14824fc..2aca535 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -136,6 +136,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 08a4d3a..75b0645 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -1246,7 +1246,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);
@@ -2840,7 +2840,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;
@@ -2915,7 +2916,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);
^ permalink raw reply related
* [RFC PATCH v2 22/32] kvm: svm: prepare to reserve asid for SEV guest
From: Brijesh Singh @ 2017-03-02 15:16 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
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 <brijesh.singh@amd.com>
Reviewed-by: Paolo Bonzini <pbonzini@redhat.com>
---
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 b581499..8d8fe62 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -507,6 +507,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;
@@ -763,6 +764,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;
native_store_gdt(&gdt_descr);
gdt = (struct desc_struct *)gdt_descr.address;
@@ -2026,7 +2028,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;
}
^ permalink raw reply related
* [RFC PATCH v2 18/32] kvm: svm: Use the hardware provided GPA instead of page walk
From: Brijesh Singh @ 2017-03-02 15:16 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
When a guest causes a NPF which requires emulation, KVM sometimes walks
the guest page tables to translate the GVA to a GPA. This is unnecessary
most of the time on AMD hardware since the hardware provides the GPA in
EXITINFO2.
The only exception cases involve string operations involving rep or
operations that use two memory locations. With rep, the GPA will only be
the value of the initial NPF and with dual memory locations we won't know
which memory address was translated into EXITINFO2.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Reviewed-by: Borislav Petkov <bp@suse.de>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
arch/x86/include/asm/kvm_emulate.h | 1 +
arch/x86/include/asm/kvm_host.h | 3 ++
arch/x86/kvm/emulate.c | 20 +++++++++++++---
arch/x86/kvm/svm.c | 2 ++
arch/x86/kvm/x86.c | 45 ++++++++++++++++++++++++++++--------
5 files changed, 57 insertions(+), 14 deletions(-)
diff --git a/arch/x86/include/asm/kvm_emulate.h b/arch/x86/include/asm/kvm_emulate.h
index e9cd7be..3e8c287 100644
--- a/arch/x86/include/asm/kvm_emulate.h
+++ b/arch/x86/include/asm/kvm_emulate.h
@@ -441,5 +441,6 @@ int emulator_task_switch(struct x86_emulate_ctxt *ctxt,
int emulate_int_real(struct x86_emulate_ctxt *ctxt, int irq);
void emulator_invalidate_register_cache(struct x86_emulate_ctxt *ctxt);
void emulator_writeback_register_cache(struct x86_emulate_ctxt *ctxt);
+bool emulator_can_use_gpa(struct x86_emulate_ctxt *ctxt);
#endif /* _ASM_X86_KVM_X86_EMULATE_H */
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index 37326b5..bff1f15 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -668,6 +668,9 @@ struct kvm_vcpu_arch {
int pending_ioapic_eoi;
int pending_external_vector;
+
+ /* GPA available (AMD only) */
+ bool gpa_available;
};
struct kvm_lpage_info {
diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c
index cedbba0..45c7306 100644
--- a/arch/x86/kvm/emulate.c
+++ b/arch/x86/kvm/emulate.c
@@ -173,6 +173,7 @@
#define NearBranch ((u64)1 << 52) /* Near branches */
#define No16 ((u64)1 << 53) /* No 16 bit operand */
#define IncSP ((u64)1 << 54) /* SP is incremented before ModRM calc */
+#define TwoMemOp ((u64)1 << 55) /* Instruction has two memory operand */
#define DstXacc (DstAccLo | SrcAccHi | SrcWrite)
@@ -4298,7 +4299,7 @@ static const struct opcode group1[] = {
};
static const struct opcode group1A[] = {
- I(DstMem | SrcNone | Mov | Stack | IncSP, em_pop), N, N, N, N, N, N, N,
+ I(DstMem | SrcNone | Mov | Stack | IncSP | TwoMemOp, em_pop), N, N, N, N, N, N, N,
};
static const struct opcode group2[] = {
@@ -4336,7 +4337,7 @@ static const struct opcode group5[] = {
I(SrcMemFAddr | ImplicitOps, em_call_far),
I(SrcMem | NearBranch, em_jmp_abs),
I(SrcMemFAddr | ImplicitOps, em_jmp_far),
- I(SrcMem | Stack, em_push), D(Undefined),
+ I(SrcMem | Stack | TwoMemOp, em_push), D(Undefined),
};
static const struct opcode group6[] = {
@@ -4556,8 +4557,8 @@ static const struct opcode opcode_table[256] = {
/* 0xA0 - 0xA7 */
I2bv(DstAcc | SrcMem | Mov | MemAbs, em_mov),
I2bv(DstMem | SrcAcc | Mov | MemAbs | PageTable, em_mov),
- I2bv(SrcSI | DstDI | Mov | String, em_mov),
- F2bv(SrcSI | DstDI | String | NoWrite, em_cmp_r),
+ I2bv(SrcSI | DstDI | Mov | String | TwoMemOp, em_mov),
+ F2bv(SrcSI | DstDI | String | NoWrite | TwoMemOp, em_cmp_r),
/* 0xA8 - 0xAF */
F2bv(DstAcc | SrcImm | NoWrite, em_test),
I2bv(SrcAcc | DstDI | Mov | String, em_mov),
@@ -5671,3 +5672,14 @@ void emulator_writeback_register_cache(struct x86_emulate_ctxt *ctxt)
{
writeback_registers(ctxt);
}
+
+bool emulator_can_use_gpa(struct x86_emulate_ctxt *ctxt)
+{
+ if (ctxt->rep_prefix && (ctxt->d & String))
+ return false;
+
+ if (ctxt->d & TwoMemOp)
+ return false;
+
+ return true;
+}
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 36d61ff..b581499 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -4184,6 +4184,8 @@ static int handle_exit(struct kvm_vcpu *vcpu)
trace_kvm_exit(exit_code, vcpu, KVM_ISA_SVM);
+ vcpu->arch.gpa_available = (exit_code == SVM_EXIT_NPF);
+
if (!is_cr_intercept(svm, INTERCEPT_CR0_WRITE))
vcpu->arch.cr0 = svm->vmcb->save.cr0;
if (npt_enabled)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 9e6a593..2099df8 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -4465,6 +4465,21 @@ int kvm_write_guest_virt_system(struct x86_emulate_ctxt *ctxt,
}
EXPORT_SYMBOL_GPL(kvm_write_guest_virt_system);
+static int vcpu_is_mmio_gpa(struct kvm_vcpu *vcpu, unsigned long gva,
+ gpa_t gpa, bool write)
+{
+ /* For APIC access vmexit */
+ if ((gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
+ return 1;
+
+ if (vcpu_match_mmio_gpa(vcpu, gpa)) {
+ trace_vcpu_match_mmio(gva, gpa, write, true);
+ return 1;
+ }
+
+ return 0;
+}
+
static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva,
gpa_t *gpa, struct x86_exception *exception,
bool write)
@@ -4491,16 +4506,7 @@ static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva,
if (*gpa == UNMAPPED_GVA)
return -1;
- /* For APIC access vmexit */
- if ((*gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE)
- return 1;
-
- if (vcpu_match_mmio_gpa(vcpu, *gpa)) {
- trace_vcpu_match_mmio(gva, *gpa, write, true);
- return 1;
- }
-
- return 0;
+ return vcpu_is_mmio_gpa(vcpu, gva, *gpa, write);
}
int emulator_write_phys(struct kvm_vcpu *vcpu, gpa_t gpa,
@@ -4597,6 +4603,22 @@ static int emulator_read_write_onepage(unsigned long addr, void *val,
int handled, ret;
bool write = ops->write;
struct kvm_mmio_fragment *frag;
+ struct x86_emulate_ctxt *ctxt = &vcpu->arch.emulate_ctxt;
+
+ /*
+ * If the exit was due to a NPF we may already have a GPA.
+ * If the GPA is present, use it to avoid the GVA to GPA table walk.
+ * Note, this cannot be used on string operations since string
+ * operation using rep will only have the initial GPA from the NPF
+ * occurred.
+ */
+ if (vcpu->arch.gpa_available &&
+ emulator_can_use_gpa(ctxt) &&
+ vcpu_is_mmio_gpa(vcpu, addr, exception->address, write) &&
+ (addr & ~PAGE_MASK) == (exception->address & ~PAGE_MASK)) {
+ gpa = exception->address;
+ goto mmio;
+ }
ret = vcpu_mmio_gva_to_gpa(vcpu, addr, &gpa, exception, write);
@@ -5613,6 +5635,9 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu,
}
restart:
+ /* Save the faulting GPA (cr2) in the address field */
+ ctxt->exception.address = cr2;
+
r = x86_emulate_insn(ctxt);
if (r == EMULATION_INTERCEPTED)
^ permalink raw reply related
* [RFC PATCH v2 23/32] kvm: introduce KVM_MEMORY_ENCRYPT_OP ioctl
From: Brijesh Singh @ 2017-03-02 15:17 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
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 <brijesh.singh@amd.com>
---
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 bff1f15..62651ad 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -1033,6 +1033,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 2099df8..6a737e9 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -3926,6 +3926,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)
{
@@ -4189,6 +4197,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 = kvm_vm_ioctl_assigned_device(kvm, ioctl, arg);
}
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index cac48ed..fef7d83 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -1281,6 +1281,8 @@ struct kvm_s390_ucas_mapping {
#define KVM_S390_GET_IRQ_STATE _IOW(KVMIO, 0xb6, struct kvm_s390_irq_state)
/* Available with KVM_CAP_X86_SMM */
#define KVM_SMI _IO(KVMIO, 0xb7)
+/* Memory Encryption Commands */
+#define KVM_MEMORY_ENCRYPT_OP _IOWR(KVMIO, 0xb8, unsigned long)
#define KVM_DEV_ASSIGN_ENABLE_IOMMU (1 << 0)
#define KVM_DEV_ASSIGN_PCI_2_3 (1 << 1)
^ permalink raw reply related
* [RFC PATCH v2 32/32] x86: kvm: Pin the guest memory when SEV is active
From: Brijesh Singh @ 2017-03-02 15:18 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
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 [1] does not provide commands to swap or migrate (move)
ciphertexts. For now we pin the memory allocated for the SEV guest. In
future when SEV key management spec provides the commands to support the
page migration we can update the KVM code to remove the pinning logical
without making any changes into userspace (qemu).
The patch pins userspace memory when a new slot is created and unpin the
memory when slot is removed.
[1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Spec.pdf
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
arch/x86/include/asm/kvm_host.h | 6 +++
arch/x86/kvm/svm.c | 93 +++++++++++++++++++++++++++++++++++++++
arch/x86/kvm/x86.c | 3 +
3 files changed, 102 insertions(+)
diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h
index fcc4710..9dc59f0 100644
--- a/arch/x86/include/asm/kvm_host.h
+++ b/arch/x86/include/asm/kvm_host.h
@@ -723,6 +723,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 pinned_memory_slot;
};
struct kvm_arch {
@@ -1043,6 +1044,11 @@ struct kvm_x86_ops {
void (*setup_mce)(struct kvm_vcpu *vcpu);
int (*memory_encryption_op)(struct kvm *kvm, void __user *argp);
+
+ void (*prepare_memory_region)(struct kvm *kvm,
+ struct kvm_memory_slot *memslot,
+ const struct kvm_userspace_memory_region *mem,
+ enum kvm_mr_change change);
};
struct kvm_arch_async_pf {
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 13996d6..ab973f9 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -498,12 +498,21 @@ static inline bool gif_set(struct vcpu_svm *svm)
}
/* Secure Encrypted Virtualization */
+struct kvm_sev_pinned_memory_slot {
+ struct list_head list;
+ unsigned long npages;
+ struct page **pages;
+ unsigned long userspace_addr;
+ short id;
+};
+
static unsigned int max_sev_asid;
static unsigned long *sev_asid_bitmap;
static void sev_deactivate_handle(struct kvm *kvm);
static void sev_decommission_handle(struct kvm *kvm);
static int sev_asid_new(void);
static void sev_asid_free(int asid);
+static void sev_unpin_memory(struct page **pages, unsigned long npages);
#define __sev_page_pa(x) ((page_to_pfn(x) << PAGE_SHIFT) | sme_me_mask)
static bool kvm_sev_enabled(void)
@@ -1544,9 +1553,25 @@ static inline int avic_free_vm_id(int id)
static void sev_vm_destroy(struct kvm *kvm)
{
+ struct list_head *pos, *q;
+ struct kvm_sev_pinned_memory_slot *pinned_slot;
+ struct list_head *head = &kvm->arch.sev_info.pinned_memory_slot;
+
if (!sev_guest(kvm))
return;
+ /* if guest memory is pinned then unpin it now */
+ if (!list_empty(head)) {
+ list_for_each_safe(pos, q, head) {
+ pinned_slot = list_entry(pos,
+ struct kvm_sev_pinned_memory_slot, list);
+ sev_unpin_memory(pinned_slot->pages,
+ pinned_slot->npages);
+ list_del(pos);
+ kfree(pinned_slot);
+ }
+ }
+
/* release the firmware resources */
sev_deactivate_handle(kvm);
sev_decommission_handle(kvm);
@@ -5663,6 +5688,8 @@ static int sev_pre_start(struct kvm *kvm, int *asid)
}
*asid = ret;
ret = 0;
+
+ INIT_LIST_HEAD(&kvm->arch.sev_info.pinned_memory_slot);
}
return ret;
@@ -6189,6 +6216,71 @@ static int sev_launch_measure(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static struct kvm_sev_pinned_memory_slot *sev_find_pinned_memory_slot(
+ struct kvm *kvm, struct kvm_memory_slot *slot)
+{
+ struct kvm_sev_pinned_memory_slot *i;
+ struct list_head *head = &kvm->arch.sev_info.pinned_memory_slot;
+
+ list_for_each_entry(i, head, list) {
+ if (i->userspace_addr == slot->userspace_addr &&
+ i->id == slot->id)
+ return i;
+ }
+
+ return NULL;
+}
+
+static void amd_prepare_memory_region(struct kvm *kvm,
+ struct kvm_memory_slot *memslot,
+ const struct kvm_userspace_memory_region *mem,
+ enum kvm_mr_change change)
+{
+ struct kvm_sev_pinned_memory_slot *pinned_slot;
+ struct list_head *head = &kvm->arch.sev_info.pinned_memory_slot;
+
+ mutex_lock(&kvm->lock);
+
+ if (!sev_guest(kvm))
+ goto unlock;
+
+ if (change == KVM_MR_CREATE) {
+
+ if (!mem->memory_size)
+ goto unlock;
+
+ pinned_slot = kmalloc(sizeof(*pinned_slot), GFP_KERNEL);
+ if (pinned_slot == NULL)
+ goto unlock;
+
+ pinned_slot->pages = sev_pin_memory(mem->userspace_addr,
+ mem->memory_size, &pinned_slot->npages);
+ if (pinned_slot->pages == NULL) {
+ kfree(pinned_slot);
+ goto unlock;
+ }
+
+ sev_clflush_pages(pinned_slot->pages, pinned_slot->npages);
+
+ pinned_slot->id = memslot->id;
+ pinned_slot->userspace_addr = mem->userspace_addr;
+ list_add_tail(&pinned_slot->list, head);
+
+ } else if (change == KVM_MR_DELETE) {
+
+ pinned_slot = sev_find_pinned_memory_slot(kvm, memslot);
+ if (!pinned_slot)
+ goto unlock;
+
+ sev_unpin_memory(pinned_slot->pages, pinned_slot->npages);
+ list_del(&pinned_slot->list);
+ kfree(pinned_slot);
+ }
+
+unlock:
+ mutex_unlock(&kvm->lock);
+}
+
static int amd_memory_encryption_cmd(struct kvm *kvm, void __user *argp)
{
int r = -ENOTTY;
@@ -6355,6 +6447,7 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.update_pi_irte = svm_update_pi_irte,
.memory_encryption_op = amd_memory_encryption_cmd,
+ .prepare_memory_region = amd_prepare_memory_region,
};
static int __init svm_init(void)
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 6a737e9..e05069d 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -8195,6 +8195,9 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm,
const struct kvm_userspace_memory_region *mem,
enum kvm_mr_change change)
{
+ if (kvm_x86_ops->prepare_memory_region)
+ kvm_x86_ops->prepare_memory_region(kvm, memslot, mem, change);
+
return 0;
}
^ permalink raw reply related
* [RFC PATCH v2 31/32] kvm: svm: Add support for SEV LAUNCH_MEASURE command
From: Brijesh Singh @ 2017-03-02 15:18 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
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 <brijesh.singh@amd.com>
---
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 64899ed..13996d6 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -6141,6 +6141,54 @@ static int sev_dbg_encrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int sev_launch_measure(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ int ret;
+ void *addr = NULL;
+ struct kvm_sev_launch_measure params;
+ struct sev_data_launch_measure *data;
+
+ 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 err_1;
+ 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 err_1;
+ }
+
+ params.length = data->length;
+ if (copy_to_user((void *)argp->data, ¶ms,
+ sizeof(struct kvm_sev_launch_measure)))
+ ret = -EFAULT;
+
+ kfree(addr);
+err_1:
+ kfree(data);
+ return ret;
+}
+
static int amd_memory_encryption_cmd(struct kvm *kvm, void __user *argp)
{
int r = -ENOTTY;
@@ -6176,6 +6224,10 @@ static int amd_memory_encryption_cmd(struct kvm *kvm, void __user *argp)
r = sev_dbg_encrypt(kvm, &sev_cmd);
break;
}
+ case KVM_SEV_LAUNCH_MEASURE: {
+ r = sev_launch_measure(kvm, &sev_cmd);
+ break;
+ }
default:
break;
}
^ permalink raw reply related
* [RFC PATCH v2 28/32] kvm: svm: Add support for SEV GUEST_STATUS command
From: Brijesh Singh @ 2017-03-02 15:18 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
The command is used for querying the SEV guest status.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
arch/x86/kvm/svm.c | 37 +++++++++++++++++++++++++++++++++++++
1 file changed, 37 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index c108064..977aa22 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -5953,6 +5953,39 @@ 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)
+{
+ int ret;
+ struct kvm_sev_guest_status params;
+ struct sev_data_guest_status *data;
+
+ 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 err_1;
+
+ params.policy = data->policy;
+ params.state = data->state;
+
+ if (copy_to_user((void *) argp->data, ¶ms,
+ sizeof(struct kvm_sev_guest_status)))
+ ret = -EFAULT;
+err_1:
+ kfree(data);
+ return ret;
+}
+
static int amd_memory_encryption_cmd(struct kvm *kvm, void __user *argp)
{
int r = -ENOTTY;
@@ -5976,6 +6009,10 @@ static int amd_memory_encryption_cmd(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;
}
^ permalink raw reply related
* [RFC PATCH v2 04/32] KVM: SVM: Add SEV feature definitions to KVM
From: Brijesh Singh @ 2017-03-02 15:12 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
Define a new KVM CPU feature for Secure Encrypted Virtualization (SEV).
The kernel will check for the presence of this feature to determine if
it is running with SEV active.
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 <thomas.lendacky@amd.com>
---
arch/x86/include/asm/svm.h | 1 +
arch/x86/include/uapi/asm/kvm_para.h | 1 +
2 files changed, 2 insertions(+)
diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h
index 2aca535..fba2a7b 100644
--- a/arch/x86/include/asm/svm.h
+++ b/arch/x86/include/asm/svm.h
@@ -137,6 +137,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;
diff --git a/arch/x86/include/uapi/asm/kvm_para.h b/arch/x86/include/uapi/asm/kvm_para.h
index 1421a65..bc2802f 100644
--- a/arch/x86/include/uapi/asm/kvm_para.h
+++ b/arch/x86/include/uapi/asm/kvm_para.h
@@ -24,6 +24,7 @@
#define KVM_FEATURE_STEAL_TIME 5
#define KVM_FEATURE_PV_EOI 6
#define KVM_FEATURE_PV_UNHALT 7
+#define KVM_FEATURE_SEV 8
/* The last 8 bits are used to indicate how to interpret the flags field
* in pvclock structure. If no bits are set, all flags are ignored.
^ permalink raw reply related
* [RFC PATCH v2 26/32] kvm: svm: Add support for SEV LAUNCH_UPDATE_DATA command
From: Brijesh Singh @ 2017-03-02 15:17 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
The command is used for encrypting the guest memory region using the VM
encryption key (VEK) created from LAUNCH_START.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
arch/x86/kvm/svm.c | 150 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 150 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index b5fa8c0..62c2b22 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -38,6 +38,8 @@
#include <linux/hashtable.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>
@@ -502,6 +504,7 @@ static void sev_deactivate_handle(struct kvm *kvm);
static void sev_decommission_handle(struct kvm *kvm);
static int sev_asid_new(void);
static void sev_asid_free(int asid);
+#define __sev_page_pa(x) ((page_to_pfn(x) << PAGE_SHIFT) | sme_me_mask)
static bool kvm_sev_enabled(void)
{
@@ -5775,6 +5778,149 @@ 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)
+{
+ struct page **pages;
+ int first, last;
+ unsigned long npages, pinned;
+
+ /* Get number of pages */
+ first = (uaddr & PAGE_MASK) >> PAGE_SHIFT;
+ last = ((uaddr + ulen - 1) & PAGE_MASK) >> PAGE_SHIFT;
+ npages = (last - first + 1);
+
+ pages = kzalloc(npages * sizeof(struct page *), GFP_KERNEL);
+ if (!pages)
+ return NULL;
+
+ /* pin the user virtual address */
+ down_read(¤t->mm->mmap_sem);
+ pinned = get_user_pages_fast(uaddr, npages, 1, pages);
+ up_read(¤t->mm->mmap_sem);
+ if (pinned != npages) {
+ printk(KERN_ERR "SEV: 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);
+ kfree(pages);
+
+ return NULL;
+}
+
+static void sev_unpin_memory(struct page **pages, unsigned long npages)
+{
+ release_pages(pages, npages, 0);
+ kfree(pages);
+}
+
+static void sev_clflush_pages(struct page *pages[], int num_pages)
+{
+ unsigned long i;
+ uint8_t *page_virtual;
+
+ if (num_pages == 0 || pages == NULL)
+ return;
+
+ for (i = 0; i < num_pages; i++) {
+ page_virtual = kmap_atomic(pages[i]);
+ clflush_cache_range(page_virtual, PAGE_SIZE);
+ kunmap_atomic(page_virtual);
+ }
+}
+
+static int sev_launch_update_data(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ struct page **inpages;
+ unsigned long uaddr, ulen;
+ int i, len, ret, offset;
+ unsigned long nr_pages;
+ struct kvm_sev_launch_update_data params;
+ struct sev_data_launch_update_data *data;
+
+ if (!sev_guest(kvm))
+ return -EINVAL;
+
+ /* Get the parameters from the user */
+ ret = -EFAULT;
+ if (copy_from_user(¶ms, (void *)argp->data,
+ sizeof(struct kvm_sev_launch_update_data)))
+ goto err_1;
+
+ uaddr = params.address;
+ ulen = params.length;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto err_1;
+ }
+
+ /* pin user pages */
+ inpages = sev_pin_memory(params.address, params.length, &nr_pages);
+ if (!inpages) {
+ ret = -ENOMEM;
+ goto err_2;
+ }
+
+ /* invalidate the cache line for these pages to ensure that DRAM
+ * has recent content before calling the SEV commands to perform
+ * the encryption.
+ */
+ sev_clflush_pages(inpages, nr_pages);
+
+ /* the array of pages returned by get_user_pages() is a page-aligned
+ * memory. Since the user buffer is probably not page-aligned, we need
+ * to calculate the offset within a page for first update entry.
+ */
+ offset = uaddr & (PAGE_SIZE - 1);
+ len = min_t(size_t, (PAGE_SIZE - offset), ulen);
+ ulen -= len;
+
+ /* update first page -
+ * special care need to be taken for the first page because we might
+ * be dealing with offset within the page
+ */
+ data->handle = sev_get_handle(kvm);
+ data->length = len;
+ data->address = __sev_page_pa(inpages[0]) + offset;
+ ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_DATA,
+ data, &argp->error);
+ if (ret)
+ goto err_3;
+
+ /* update remaining pages */
+ for (i = 1; i < nr_pages; i++) {
+
+ len = min_t(size_t, PAGE_SIZE, ulen);
+ ulen -= len;
+ data->length = len;
+ data->address = __sev_page_pa(inpages[i]);
+ ret = sev_issue_cmd(kvm, SEV_CMD_LAUNCH_UPDATE_DATA,
+ data, &argp->error);
+ if (ret)
+ goto err_3;
+ }
+
+ /* mark pages dirty */
+ for (i = 0; i < nr_pages; i++) {
+ set_page_dirty_lock(inpages[i]);
+ mark_page_accessed(inpages[i]);
+ }
+err_3:
+ sev_unpin_memory(inpages, nr_pages);
+err_2:
+ kfree(data);
+err_1:
+ return ret;
+}
+
static int amd_memory_encryption_cmd(struct kvm *kvm, void __user *argp)
{
int r = -ENOTTY;
@@ -5790,6 +5936,10 @@ static int amd_memory_encryption_cmd(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;
}
^ permalink raw reply related
* [RFC PATCH v2 21/32] crypto: ccp: Add Secure Encrypted Virtualization (SEV) interface support
From: Brijesh Singh @ 2017-03-02 15:16 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
The Secure Encrypted Virtualization (SEV) interface allows the memory
contents of a virtual machine (VM) to be transparently encrypted with
a key unique to the guest.
The interface provides:
- /dev/sev device and ioctl (SEV_ISSUE_CMD) to execute the platform
provisioning commands from the userspace.
- in-kernel API's to encrypt the guest memory region. The in-kernel APIs
will be used by KVM to bootstrap and debug the SEV guest.
SEV key management spec is available here [1]
[1] http://support.amd.com/TechDocs/55766_SEV-KM%20API_Specification.pdf
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
drivers/crypto/ccp/Kconfig | 7
drivers/crypto/ccp/Makefile | 1
drivers/crypto/ccp/psp-dev.h | 6
drivers/crypto/ccp/sev-dev.c | 348 ++++++++++++++++++++++
drivers/crypto/ccp/sev-dev.h | 67 ++++
drivers/crypto/ccp/sev-ops.c | 324 ++++++++++++++++++++
include/linux/psp-sev.h | 672 ++++++++++++++++++++++++++++++++++++++++++
include/uapi/linux/Kbuild | 1
include/uapi/linux/psp-sev.h | 123 ++++++++
9 files changed, 1546 insertions(+), 3 deletions(-)
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 59c207e..67d1917 100644
--- a/drivers/crypto/ccp/Kconfig
+++ b/drivers/crypto/ccp/Kconfig
@@ -41,4 +41,11 @@ config CRYPTO_DEV_PSP
help
Provide the interface for AMD Platform Security Processor (PSP) device.
+config CRYPTO_DEV_SEV
+ bool "Secure Encrypted Virtualization (SEV) interface"
+ default y
+ help
+ Provide the kernel and userspace (/dev/sev) interface to issue the
+ Secure Encrypted Virtualization (SEV) commands.
+
endif
diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile
index 12e569d..4c4e77e 100644
--- a/drivers/crypto/ccp/Makefile
+++ b/drivers/crypto/ccp/Makefile
@@ -7,6 +7,7 @@ ccp-$(CONFIG_CRYPTO_DEV_CCP) += ccp-dev.o \
ccp-dev-v5.o \
ccp-dmaengine.o
ccp-$(CONFIG_CRYPTO_DEV_PSP) += psp-dev.o
+ccp-$(CONFIG_CRYPTO_DEV_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.h b/drivers/crypto/ccp/psp-dev.h
index bbd3d96..fd67b14 100644
--- a/drivers/crypto/ccp/psp-dev.h
+++ b/drivers/crypto/ccp/psp-dev.h
@@ -70,14 +70,14 @@ int psp_free_sev_irq(struct psp_device *psp, void *data);
struct psp_device *psp_get_master_device(void);
-#ifdef CONFIG_AMD_SEV
+#ifdef CONFIG_CRYPTO_DEV_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
+#else /* !CONFIG_CRYPTO_DEV_SEV */
static inline int sev_dev_init(struct psp_device *psp)
{
@@ -96,7 +96,7 @@ static inline int sev_dev_suspend(struct psp_device *psp, pm_message_t state)
return -ENODEV;
}
-#endif /* __AMD_SEV_H */
+#endif /* CONFIG_CRYPTO_DEV_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..a67e2d7
--- /dev/null
+++ b/drivers/crypto/ccp/sev-dev.c
@@ -0,0 +1,348 @@
+/*
+ * AMD Secure Encrypted Virtualization (SEV) interface
+ *
+ * Copyright (C) 2016 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/wait.h>
+#include <linux/jiffies.h>
+
+#include "psp-dev.h"
+#include "sev-dev.h"
+
+extern struct file_operations sev_fops;
+
+static LIST_HEAD(sev_devs);
+static DEFINE_SPINLOCK(sev_devs_lock);
+static atomic_t sev_id;
+
+static unsigned int psp_poll;
+module_param(psp_poll, uint, 0444);
+MODULE_PARM_DESC(psp_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 timeout,
+ unsigned int *reg)
+{
+ unsigned long jiffie_timeout = timeout;
+ long ret;
+
+ jiffie_timeout *= HZ;
+
+ sev->int_rcvd = 0;
+
+ ret = wait_event_interruptible_timeout(sev->int_queue, sev->int_rcvd,
+ jiffie_timeout);
+ if (ret <= 0) {
+ dev_err(sev->dev, "sev command (%#x) timed out\n",
+ *reg >> PSP_CMDRESP_CMD_SHIFT);
+ return -ETIMEDOUT;
+ }
+
+ *reg = ioread32(sev->io_regs + PSP_CMDRESP);
+
+ return 0;
+}
+
+static int sev_wait_cmd(struct sev_device *sev, unsigned int timeout,
+ unsigned int *reg)
+{
+ return (*reg & PSP_CMDRESP_IOC) ? sev_wait_cmd_ioc(sev, timeout, reg)
+ : sev_wait_cmd_poll(sev, timeout, 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_interruptible(&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;
+}
+
+int sev_issue_cmd(int cmd, void *data, unsigned int timeout, 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);
+
+ /* 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 |= psp_poll ? 0 : PSP_CMDRESP_IOC;
+ iowrite32(reg, sev->io_regs + PSP_CMDRESP);
+
+ ret = sev_wait_cmd(sev, timeout, ®);
+ 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);
+
+ return ret;
+}
+
+int sev_platform_init(struct sev_data_init *data, int *error)
+{
+ return sev_issue_cmd(SEV_CMD_INIT, data, SEV_DEFAULT_TIMEOUT, error);
+}
+EXPORT_SYMBOL_GPL(sev_platform_init);
+
+int sev_platform_shutdown(int *error)
+{
+ return sev_issue_cmd(SEV_CMD_SHUTDOWN, 0, SEV_DEFAULT_TIMEOUT, 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,
+ SEV_DEFAULT_TIMEOUT, error);
+}
+EXPORT_SYMBOL_GPL(sev_platform_status);
+
+int sev_issue_cmd_external_user(struct file *filep, unsigned int cmd,
+ void *data, int timeout, int *error)
+{
+ if (!filep || filep->f_op != &sev_fops)
+ return -EBADF;
+
+ return sev_issue_cmd(cmd, data,
+ timeout ? timeout : SEV_DEFAULT_TIMEOUT, 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,
+ SEV_DEFAULT_TIMEOUT, 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,
+ SEV_DEFAULT_TIMEOUT, 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,
+ SEV_DEFAULT_TIMEOUT, error);
+}
+EXPORT_SYMBOL_GPL(sev_guest_decommission);
+
+int sev_guest_df_flush(int *error)
+{
+ return sev_issue_cmd(SEV_CMD_DF_FLUSH, 0,
+ SEV_DEFAULT_TIMEOUT, 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..0df6ead
--- /dev/null
+++ b/drivers/crypto/ccp/sev-dev.h
@@ -0,0 +1,67 @@
+/*
+ * AMD Secure Encrypted Virtualization (SEV) interface
+ *
+ * Copyright (C) 2013,2016 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __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, unsigned int timeout, 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..727a8db
--- /dev/null
+++ b/drivers/crypto/ccp/sev-ops.c
@@ -0,0 +1,324 @@
+/*
+ * AMD Secure Encrypted Virtualization (SEV) command interface
+ *
+ * Copyright (C) 2016 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/uaccess.h>
+
+#include <uapi/linux/psp-sev.h>
+
+#include "psp-dev.h"
+#include "sev-dev.h"
+
+static int sev_ioctl_init(struct sev_issue_cmd *argp)
+{
+ int ret;
+ struct sev_data_init *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ ret = sev_platform_init(data, &argp->error);
+
+ kfree(data);
+ return ret;
+}
+
+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 ret;
+ 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;
+
+ /* copy PEK certificate from userspace */
+ if (input.address && input.length) {
+ csr_addr = kmalloc(input.length, GFP_KERNEL);
+ if (!csr_addr) {
+ ret = -ENOMEM;
+ goto e_err;
+ }
+ if (copy_from_user(csr_addr, (void *)input.address,
+ input.length)) {
+ ret = -EFAULT;
+ goto e_csr_free;
+ }
+
+ data->address = __psp_pa(csr_addr);
+ data->length = input.length;
+ }
+
+ ret = sev_issue_cmd(SEV_CMD_PEK_CSR,
+ data, SEV_DEFAULT_TIMEOUT, &argp->error);
+
+ input.length = data->length;
+
+ /* copy PEK certificate length to userspace */
+ if (copy_to_user((void *)argp->data, &input,
+ sizeof(struct sev_user_data_pek_csr)))
+ ret = -EFAULT;
+e_csr_free:
+ kfree(csr_addr);
+e_err:
+ kfree(data);
+ return ret;
+}
+
+static int sev_ioctl_pek_cert_import(struct sev_issue_cmd *argp)
+{
+ int ret;
+ struct sev_data_pek_cert_import *data;
+ struct sev_user_data_pek_cert_import input;
+ void *pek_cert, *oca_cert;
+
+ 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;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* copy PEK certificate from userspace */
+ pek_cert = kmalloc(input.pek_cert_length, GFP_KERNEL);
+ if (!pek_cert) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+ if (copy_from_user(pek_cert, (void *)input.pek_cert_address,
+ input.pek_cert_length)) {
+ ret = -EFAULT;
+ goto e_free_pek_cert;
+ }
+
+ data->pek_cert_address = __psp_pa(pek_cert);
+ data->pek_cert_length = input.pek_cert_length;
+
+ /* copy OCA certificate from userspace */
+ oca_cert = kmalloc(input.oca_cert_length, GFP_KERNEL);
+ if (!oca_cert) {
+ ret = -ENOMEM;
+ goto e_free_pek_cert;
+ }
+ if (copy_from_user(oca_cert, (void *)input.oca_cert_address,
+ input.oca_cert_length)) {
+ ret = -EFAULT;
+ goto e_free_oca_cert;
+ }
+
+ 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, SEV_DEFAULT_TIMEOUT, &argp->error);
+e_free_oca_cert:
+ kfree(oca_cert);
+e_free_pek_cert:
+ kfree(pek_cert);
+e_free:
+ kfree(data);
+ return ret;
+}
+
+static int sev_ioctl_pdh_cert_export(struct sev_issue_cmd *argp)
+{
+ int ret;
+ 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;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* copy pdh certificate from userspace */
+ if (input.pdh_cert_length && input.pdh_cert_address) {
+ pdh_cert = kmalloc(input.pdh_cert_length, GFP_KERNEL);
+ if (!pdh_cert) {
+ ret = -ENOMEM;
+ goto e_free;
+ }
+ if (copy_from_user(pdh_cert, (void *)input.pdh_cert_address,
+ input.pdh_cert_length)) {
+ ret = -EFAULT;
+ goto e_free_pdh_cert;
+ }
+
+ data->pdh_cert_address = __psp_pa(pdh_cert);
+ data->pdh_cert_length = input.pdh_cert_length;
+ }
+
+ /* copy cert_chain certificate from userspace */
+ if (input.cert_chain_length && input.cert_chain_address) {
+ cert_chain = kmalloc(input.cert_chain_length, GFP_KERNEL);
+ if (!cert_chain) {
+ ret = -ENOMEM;
+ goto e_free_pdh_cert;
+ }
+ if (copy_from_user(cert_chain, (void *)input.cert_chain_address,
+ input.cert_chain_length)) {
+ ret = -EFAULT;
+ goto e_free_cert_chain;
+ }
+
+ 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, SEV_DEFAULT_TIMEOUT, &argp->error);
+
+ input.cert_chain_length = data->cert_chain_length;
+ input.pdh_cert_length = data->pdh_cert_length;
+
+ /* copy certificate length to userspace */
+ if (copy_to_user((void *)argp->data, &input,
+ sizeof(struct sev_user_data_pek_csr)))
+ ret = -EFAULT;
+
+e_free_cert_chain:
+ kfree(cert_chain);
+e_free_pdh_cert:
+ kfree(pdh_cert);
+e_free:
+ 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_INIT: {
+ ret = sev_ioctl_init(&input);
+ break;
+ }
+ case SEV_USER_CMD_SHUTDOWN: {
+ ret = sev_platform_shutdown(&input.error);
+ break;
+ }
+ case SEV_USER_CMD_FACTORY_RESET: {
+ ret = sev_issue_cmd(SEV_CMD_FACTORY_RESET, 0,
+ SEV_DEFAULT_TIMEOUT, &input.error);
+ break;
+ }
+ case SEV_USER_CMD_PLATFORM_STATUS: {
+ ret = sev_ioctl_platform_status(&input);
+ break;
+ }
+ case SEV_USER_CMD_PEK_GEN: {
+ ret = sev_issue_cmd(SEV_CMD_PEK_GEN, 0,
+ SEV_DEFAULT_TIMEOUT, &input.error);
+ break;
+ }
+ case SEV_USER_CMD_PDH_GEN: {
+ ret = sev_issue_cmd(SEV_CMD_PDH_GEN, 0,
+ SEV_DEFAULT_TIMEOUT, &input.error);
+ 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;
+
+ misc->minor = MISC_DYNAMIC_MINOR;
+ misc->name = sev->name;
+ misc->fops = &sev_fops;
+
+ return misc_register(misc);
+}
+
+void sev_ops_destroy(struct sev_device *sev)
+{
+ misc_deregister(&sev->misc);
+}
+
diff --git a/include/linux/psp-sev.h b/include/linux/psp-sev.h
new file mode 100644
index 0000000..acce6ed
--- /dev/null
+++ b/include/linux/psp-sev.h
@@ -0,0 +1,672 @@
+/*
+ * AMD Secure Encrypted Virtualization (SEV) driver interface
+ *
+ * Copyright (C) 2016 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PSP_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 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_GEN = 0x008,
+ SEV_CMD_PDH_CERT_EXPORT = 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_data_address; /* In */
+ u32 session_data_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
+ * @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_data_address: physical address containing Session data
+ * @session_length: length of session data
+ */
+struct sev_data_send_start {
+ u32 handle; /* In */
+ u32 reserved1;
+ u64 pdh_cert_address; /* In */
+ u32 pdh_cert_length; /* In/Out */
+ u32 reserved2;
+ u64 plat_cert_address; /* In */
+ u32 plat_cert_length; /* In/Out */
+ u32 reserved3;
+ u64 amd_cert_address; /* In */
+ u32 amd_cert_length; /* In/Out */
+ u32 reserved4;
+ u64 session_data_address; /* In */
+ u32 session_data_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 reserved1;
+ u64 pdh_cert_address; /* In */
+ u32 pdh_cert_length; /* In */
+ u32 reserved2;
+ u64 session_data_address; /* In */
+ u32 session_data_length; /* In/Out */
+};
+
+/**
+ * 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_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
+ * @timeout - If zero then use default timeout
+ * @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 timeout, 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_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 timeout, 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_SEV */
+
+#endif /* __PSP_SEV_H__ */
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index f330ba4..2e15ea7 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -481,3 +481,4 @@ header-y += xilinx-v4l2-controls.h
header-y += zorro.h
header-y += zorro_ids.h
header-y += userfaultfd.h
+header-y += 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..050976d
--- /dev/null
+++ b/include/uapi/linux/psp-sev.h
@@ -0,0 +1,123 @@
+
+/*
+ * Userspace interface for AMD Secure Encrypted Virtualization (SEV)
+ *
+ * Copyright (C) 2016 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PSP_SEV_USER_H__
+#define __PSP_SEV_USER_H__
+
+#include <linux/types.h>
+
+/**
+ * SEV platform commands
+ */
+enum {
+ SEV_USER_CMD_INIT = 0,
+ SEV_USER_CMD_SHUTDOWN,
+ SEV_USER_CMD_FACTORY_RESET,
+ 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_init - INIT command parameters
+ *
+ * @flags: processing flags
+ */
+struct sev_user_data_init {
+ __u32 flags; /* In */
+};
+
+/**
+ * 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 */
+};
+
+/**
+ * q
+ * 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 */
+
^ permalink raw reply related
* [RFC PATCH v2 20/32] crypto: ccp: Add Platform Security Processor (PSP) interface support
From: Brijesh Singh @ 2017-03-02 15:16 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
AMD Platform Security Processor (PSP) is a dedicated processor that
provides the support for encrypting the guest memory in a Secure Encrypted
Virtualiztion (SEV) mode, along with software-based Tursted Executation
Environment (TEE) to enable the third-party tursted applications.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
drivers/crypto/ccp/Kconfig | 7 +
drivers/crypto/ccp/Makefile | 1
drivers/crypto/ccp/psp-dev.c | 211 ++++++++++++++++++++++++++++++++++++++++++
drivers/crypto/ccp/psp-dev.h | 102 ++++++++++++++++++++
drivers/crypto/ccp/sp-dev.c | 16 +++
drivers/crypto/ccp/sp-dev.h | 34 +++++++
drivers/crypto/ccp/sp-pci.c | 4 +
7 files changed, 374 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 bc08f03..59c207e 100644
--- a/drivers/crypto/ccp/Kconfig
+++ b/drivers/crypto/ccp/Kconfig
@@ -34,4 +34,11 @@ config CRYPTO_DEV_CCP
Provides the interface to use the AMD Cryptographic Coprocessor
which can be used to offload encryption operations such as SHA,
AES and more.
+
+config CRYPTO_DEV_PSP
+ bool "Platform Security Processor interface"
+ default y
+ help
+ Provide the interface for AMD Platform Security Processor (PSP) device.
+
endif
diff --git a/drivers/crypto/ccp/Makefile b/drivers/crypto/ccp/Makefile
index 8127e18..12e569d 100644
--- a/drivers/crypto/ccp/Makefile
+++ b/drivers/crypto/ccp/Makefile
@@ -6,6 +6,7 @@ ccp-$(CONFIG_CRYPTO_DEV_CCP) += ccp-dev.o \
ccp-dev-v3.o \
ccp-dev-v5.o \
ccp-dmaengine.o
+ccp-$(CONFIG_CRYPTO_DEV_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..6f64aa7
--- /dev/null
+++ b/drivers/crypto/ccp/psp-dev.c
@@ -0,0 +1,211 @@
+/*
+ * AMD Platform Security Processor (PSP) interface
+ *
+ * Copyright (C) 2016 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/hw_random.h>
+#include <linux/ccp.h>
+
+#include "sp-dev.h"
+#include "psp-dev.h"
+
+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);
+ }
+
+ /* 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);
+
+ sev_dev_init(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_data->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;
+
+ sev_dev_destroy(psp);
+
+ sp_free_psp_irq(sp, 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;
+}
+
+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..bbd3d96
--- /dev/null
+++ b/drivers/crypto/ccp/psp-dev.h
@@ -0,0 +1,102 @@
+/*
+ * AMD Platform Security Processor (PSP) interface driver
+ *
+ * Copyright (C) 2016 Advanced Micro Devices, Inc.
+ *
+ * Author: Brijesh Singh <brijesh.singh@amd.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __PSP_DEV_H__
+#define __PSP_DEV_H__
+
+#include <linux/device.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/list.h>
+#include <linux/wait.h>
+#include <linux/dmapool.h>
+#include <linux/hw_random.h>
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+#include <linux/dmaengine.h>
+
+#include "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;
+
+ void *sev_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);
+
+struct psp_device *psp_get_master_device(void);
+
+#ifdef CONFIG_AMD_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
+
+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 /* __AMD_SEV_H */
+
+#endif /* __PSP_DEV_H */
+
diff --git a/drivers/crypto/ccp/sp-dev.c b/drivers/crypto/ccp/sp-dev.c
index e47fb8e..975a435 100644
--- a/drivers/crypto/ccp/sp-dev.c
+++ b/drivers/crypto/ccp/sp-dev.c
@@ -212,6 +212,8 @@ int sp_init(struct sp_device *sp)
if (sp->dev_data->ccp_vdata)
ccp_dev_init(sp);
+ if (sp->dev_data->psp_vdata)
+ psp_dev_init(sp);
return 0;
}
@@ -220,6 +222,9 @@ void sp_destroy(struct sp_device *sp)
if (sp->dev_data->ccp_vdata)
ccp_dev_destroy(sp);
+ if (sp->dev_data->psp_vdata)
+ psp_dev_destroy(sp);
+
sp_del_device(sp);
}
@@ -233,6 +238,12 @@ int sp_suspend(struct sp_device *sp, pm_message_t state)
return ret;
}
+ if (sp->dev_data->psp_vdata) {
+ ret = psp_dev_suspend(sp, state);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
@@ -246,6 +257,11 @@ int sp_resume(struct sp_device *sp)
return ret;
}
+ if (sp->dev_data->psp_vdata) {
+ ret = psp_dev_resume(sp);
+ if (ret)
+ return ret;
+ }
return 0;
}
diff --git a/drivers/crypto/ccp/sp-dev.h b/drivers/crypto/ccp/sp-dev.h
index 9a8a8f8..aeff7a0 100644
--- a/drivers/crypto/ccp/sp-dev.h
+++ b/drivers/crypto/ccp/sp-dev.h
@@ -40,12 +40,18 @@ struct ccp_vdata {
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_data {
const unsigned int bar;
const struct ccp_vdata *ccp_vdata;
- const void *psp_vdata;
+ const struct psp_vdata *psp_vdata;
};
struct sp_device {
@@ -137,4 +143,30 @@ static inline int ccp_dev_resume(struct sp_device *sp)
#endif /* CONFIG_CRYPTO_DEV_CCP */
+#ifdef CONFIG_CRYPTO_DEV_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_CCP */
+
+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_CCP */
+
#endif
diff --git a/drivers/crypto/ccp/sp-pci.c b/drivers/crypto/ccp/sp-pci.c
index 0960e2d..4999662 100644
--- a/drivers/crypto/ccp/sp-pci.c
+++ b/drivers/crypto/ccp/sp-pci.c
@@ -271,6 +271,7 @@ static int sp_pci_resume(struct pci_dev *pdev)
extern struct ccp_vdata ccpv3_pci;
extern struct ccp_vdata ccpv5a;
extern struct ccp_vdata ccpv5b;
+extern struct psp_vdata psp_entry;
static const struct sp_dev_data dev_data[] = {
{
@@ -284,6 +285,9 @@ static const struct sp_dev_data dev_data[] = {
#ifdef CONFIG_CRYPTO_DEV_CCP
.ccp_vdata = &ccpv5a,
#endif
+#ifdef CONFIG_CRYPTO_DEV_PSP
+ .psp_vdata = &psp_entry
+#endif
},
{
.bar = 2,
^ permalink raw reply related
* [RFC PATCH v2 25/32] kvm: svm: Add support for SEV LAUNCH_START command
From: Brijesh Singh @ 2017-03-02 15:17 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
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
public DH certificates, and session data. The VEK will be used to encrypt
the guest memory.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
arch/x86/kvm/svm.c | 302 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 301 insertions(+), 1 deletion(-)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index fb63398..b5fa8c0 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -37,6 +37,7 @@
#include <linux/amd-iommu.h>
#include <linux/hashtable.h>
#include <linux/psp-sev.h>
+#include <linux/file.h>
#include <asm/apic.h>
#include <asm/perf_event.h>
@@ -497,6 +498,10 @@ static inline bool gif_set(struct vcpu_svm *svm)
/* Secure Encrypted Virtualization */
static unsigned int max_sev_asid;
static unsigned long *sev_asid_bitmap;
+static void sev_deactivate_handle(struct kvm *kvm);
+static void sev_decommission_handle(struct kvm *kvm);
+static int sev_asid_new(void);
+static void sev_asid_free(int asid);
static bool kvm_sev_enabled(void)
{
@@ -1534,6 +1539,17 @@ static inline int avic_free_vm_id(int id)
return 0;
}
+static void sev_vm_destroy(struct kvm *kvm)
+{
+ if (!sev_guest(kvm))
+ return;
+
+ /* release the firmware resources */
+ sev_deactivate_handle(kvm);
+ sev_decommission_handle(kvm);
+ sev_asid_free(sev_get_asid(kvm));
+}
+
static void avic_vm_destroy(struct kvm *kvm)
{
unsigned long flags;
@@ -1551,6 +1567,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;
@@ -5502,6 +5524,282 @@ static inline void avic_post_state_restore(struct kvm_vcpu *vcpu)
avic_handle_ldr_update(vcpu);
}
+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 cpu, pos;
+ struct svm_cpu_data *sd;
+
+ 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_issue_cmd(struct kvm *kvm, int id, void *data, int *error)
+{
+ int ret;
+ struct fd f;
+ int fd = sev_get_fd(kvm);
+
+ f = fdget(fd);
+ if (!f.file)
+ return -EBADF;
+
+ ret = sev_issue_cmd_external_user(f.file, id, data, 0, error);
+ fdput(f);
+
+ return ret;
+}
+
+static void sev_decommission_handle(struct kvm *kvm)
+{
+ int ret, error;
+ struct sev_data_decommission *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->handle = sev_get_handle(kvm);
+ ret = sev_guest_decommission(data, &error);
+ if (ret)
+ pr_err("SEV: DECOMMISSION %d (%#x)\n", ret, error);
+
+ kfree(data);
+}
+
+static void sev_deactivate_handle(struct kvm *kvm)
+{
+ int ret, error;
+ struct sev_data_deactivate *data;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return;
+
+ data->handle = sev_get_handle(kvm);
+ ret = sev_guest_deactivate(data, &error);
+ if (ret) {
+ pr_err("SEV: DEACTIVATE %d (%#x)\n", ret, error);
+ goto buffer_free;
+ }
+
+ wbinvd_on_all_cpus();
+
+ ret = sev_guest_df_flush(&error);
+ if (ret)
+ pr_err("SEV: DF_FLUSH %d (%#x)\n", ret, error);
+
+buffer_free:
+ kfree(data);
+}
+
+static int sev_activate_asid(unsigned int handle, int asid, int *error)
+{
+ int ret;
+ struct sev_data_activate *data;
+
+ wbinvd_on_all_cpus();
+
+ ret = sev_guest_df_flush(error);
+ if (ret) {
+ pr_err("SEV: DF_FLUSH %d (%#x)\n", ret, *error);
+ 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)
+ pr_err("SEV: ACTIVATE %d (%#x)\n", ret, *error);
+
+ kfree(data);
+ return ret;
+}
+
+static int sev_pre_start(struct kvm *kvm, int *asid)
+{
+ int ret;
+
+ /* If guest has active SEV handle then deactivate before creating the
+ * encryption context.
+ */
+ if (sev_guest(kvm)) {
+ sev_deactivate_handle(kvm);
+ sev_decommission_handle(kvm);
+ *asid = sev_get_asid(kvm); /* reuse the asid */
+ ret = 0;
+ } else {
+ /* Allocate new asid for this launch */
+ ret = sev_asid_new();
+ if (ret < 0) {
+ pr_err("SEV: failed to get free asid\n");
+ return ret;
+ }
+ *asid = ret;
+ ret = 0;
+ }
+
+ return ret;
+}
+
+static int sev_post_start(struct kvm *kvm, int asid, int handle,
+ int sev_fd, int *error)
+{
+ int ret;
+
+ /* activate asid */
+ ret = sev_activate_asid(handle, asid, error);
+ if (ret)
+ return ret;
+
+ kvm->arch.sev_info.handle = handle;
+ kvm->arch.sev_info.asid = asid;
+ kvm->arch.sev_info.sev_fd = sev_fd;
+
+ return 0;
+}
+
+static int sev_launch_start(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ int ret, asid = 0;
+ void *dh_cert_addr = NULL;
+ void *session_addr = NULL;
+ struct kvm_sev_launch_start params;
+ struct sev_data_launch_start *start;
+ int *error = &argp->error;
+ struct fd f;
+
+ f = fdget(argp->sev_fd);
+ if (!f.file)
+ return -EBADF;
+
+ /* Get parameter from the user */
+ ret = -EFAULT;
+ if (copy_from_user(¶ms, (void *)argp->data,
+ sizeof(struct kvm_sev_launch_start)))
+ goto err_1;
+
+ ret = -ENOMEM;
+ start = kzalloc(sizeof(*start), GFP_KERNEL);
+ if (!start)
+ goto err_1;
+
+ ret = sev_pre_start(kvm, &asid);
+ if (ret)
+ goto err_2;
+
+ start->handle = params.handle;
+ start->policy = params.policy;
+
+ /* Copy DH certificate from userspace */
+ if (params.dh_cert_length && params.dh_cert_data) {
+ dh_cert_addr = kmalloc(params.dh_cert_length, GFP_KERNEL);
+ if (!dh_cert_addr) {
+ ret = -EFAULT;
+ goto err_3;
+ }
+ if (copy_from_user(dh_cert_addr, (void *)params.dh_cert_data,
+ params.dh_cert_length)) {
+ ret = -EFAULT;
+ goto err_3;
+ }
+
+ start->dh_cert_address = __psp_pa(dh_cert_addr);
+ start->dh_cert_length = params.dh_cert_length;
+ }
+
+ /* Copy session data from userspace */
+ if (params.session_length && params.session_data) {
+ session_addr = kmalloc(params.dh_cert_length, GFP_KERNEL);
+ if (!session_addr) {
+ ret = -EFAULT;
+ goto err_3;
+ }
+ if (copy_from_user(session_addr, (void *)params.session_data,
+ params.session_length)) {
+ ret = -EFAULT;
+ goto err_3;
+ }
+ start->session_data_address = __psp_pa(session_addr);
+ start->session_data_length = params.session_length;
+ }
+
+ /* launch start */
+ ret = sev_issue_cmd_external_user(f.file, SEV_CMD_LAUNCH_START,
+ start, 0, error);
+ if (ret) {
+ pr_err("SEV: LAUNCH_START ret=%d (%#010x)\n", ret, *error);
+ goto err_3;
+ }
+
+ ret = sev_post_start(kvm, asid, start->handle, argp->sev_fd, error);
+ if (ret)
+ goto err_3;
+
+ params.handle = start->handle;
+ if (copy_to_user((void *) argp->data, ¶ms,
+ sizeof(struct kvm_sev_launch_start)))
+ ret = -EFAULT;
+err_3:
+ if (ret && asid) /* free asid if we have encountered error */
+ sev_asid_free(asid);
+ kfree(dh_cert_addr);
+ kfree(session_addr);
+err_2:
+ kfree(start);
+err_1:
+ fdput(f);
+ return ret;
+}
+
+static int amd_memory_encryption_cmd(struct kvm *kvm, void __user *argp)
+{
+ int r = -ENOTTY;
+ struct kvm_sev_cmd sev_cmd;
+
+ 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_LAUNCH_START: {
+ r = sev_launch_start(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,
@@ -5518,7 +5816,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,
@@ -5617,6 +5915,8 @@ static struct kvm_x86_ops svm_x86_ops __ro_after_init = {
.pmu_ops = &amd_pmu_ops,
.deliver_posted_interrupt = svm_deliver_avic_intr,
.update_pi_irte = svm_update_pi_irte,
+
+ .memory_encryption_op = amd_memory_encryption_cmd,
};
static int __init svm_init(void)
^ permalink raw reply related
* [RFC PATCH v2 29/32] kvm: svm: Add support for SEV DEBUG_DECRYPT command
From: Brijesh Singh @ 2017-03-02 15:18 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
The command is used to decrypt guest memory region for debug purposes.
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
arch/x86/kvm/svm.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index 977aa22..ce8819a 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -5986,6 +5986,78 @@ static int sev_guest_status(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int __sev_dbg_decrypt_page(struct kvm *kvm, unsigned long src,
+ void *dst, int *error)
+{
+ int ret;
+ struct page **inpages;
+ struct sev_data_dbg *data;
+ unsigned long npages;
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ inpages = sev_pin_memory(src, PAGE_SIZE, &npages);
+ if (!inpages) {
+ ret = -ENOMEM;
+ goto err_1;
+ }
+
+ data->handle = sev_get_handle(kvm);
+ data->dst_addr = __psp_pa(dst);
+ data->src_addr = __sev_page_pa(inpages[0]);
+ data->length = PAGE_SIZE;
+
+ ret = sev_issue_cmd(kvm, SEV_CMD_DBG_DECRYPT, data, error);
+ if (ret)
+ printk(KERN_ERR "SEV: DEBUG_DECRYPT %d (%#010x)\n",
+ ret, *error);
+ sev_unpin_memory(inpages, npages);
+err_1:
+ kfree(data);
+ return ret;
+}
+
+static int sev_dbg_decrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ void *data;
+ int ret, offset, len;
+ struct kvm_sev_dbg debug;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ if (copy_from_user(&debug, (void *)argp->data,
+ sizeof(struct kvm_sev_dbg)))
+ return -EFAULT;
+ /*
+ * TODO: add support for decrypting length which crosses the
+ * page boundary.
+ */
+ offset = debug.src_addr & (PAGE_SIZE - 1);
+ if (offset + debug.length > PAGE_SIZE)
+ return -EINVAL;
+
+ data = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ /* decrypt full page */
+ ret = __sev_dbg_decrypt_page(kvm, debug.src_addr & PAGE_MASK,
+ data, &argp->error);
+ if (ret)
+ goto err_1;
+
+ /* we have decrypted full page but copy request length */
+ len = min_t(size_t, (PAGE_SIZE - offset), debug.length);
+ if (copy_to_user((uint8_t *)debug.dst_addr, data + offset, len))
+ ret = -EFAULT;
+err_1:
+ free_page((unsigned long)data);
+ return ret;
+}
+
static int amd_memory_encryption_cmd(struct kvm *kvm, void __user *argp)
{
int r = -ENOTTY;
@@ -6013,6 +6085,10 @@ static int amd_memory_encryption_cmd(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;
}
^ permalink raw reply related
* [RFC PATCH v2 05/32] x86: Use encrypted access of BOOT related data with SEV
From: Brijesh Singh @ 2017-03-02 15:12 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
When Secure Encrypted Virtualization (SEV) is active, BOOT data (such as
EFI related data, setup data) is encrypted and needs to be accessed as
such when mapped. Update the architecture override in early_memremap to
keep the encryption attribute when mapping this data.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
arch/x86/mm/ioremap.c | 36 +++++++++++++++++++++++++++++++-----
1 file changed, 31 insertions(+), 5 deletions(-)
diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
index c6cb921..c400ab5 100644
--- a/arch/x86/mm/ioremap.c
+++ b/arch/x86/mm/ioremap.c
@@ -462,12 +462,31 @@ static bool memremap_is_setup_data(resource_size_t phys_addr,
}
/*
- * This function determines if an address should be mapped encrypted.
- * Boot setup data, EFI data and E820 areas are checked in making this
- * determination.
+ * This function determines if an address should be mapped encrypted when
+ * SEV is active. E820 areas are checked in making this determination.
*/
-static bool memremap_should_map_encrypted(resource_size_t phys_addr,
- unsigned long size)
+static bool memremap_sev_should_map_encrypted(resource_size_t phys_addr,
+ unsigned long size)
+{
+ /* Check if the address is in persistent memory */
+ switch (e820__get_entry_type(phys_addr, phys_addr + size - 1)) {
+ case E820_TYPE_PMEM:
+ case E820_TYPE_PRAM:
+ return false;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+/*
+ * This function determines if an address should be mapped encrypted when
+ * SME is active. Boot setup data, EFI data and E820 areas are checked in
+ * making this determination.
+ */
+static bool memremap_sme_should_map_encrypted(resource_size_t phys_addr,
+ unsigned long size)
{
/*
* SME is not active, return true:
@@ -508,6 +527,13 @@ static bool memremap_should_map_encrypted(resource_size_t phys_addr,
return true;
}
+static bool memremap_should_map_encrypted(resource_size_t phys_addr,
+ unsigned long size)
+{
+ return sev_active() ? memremap_sev_should_map_encrypted(phys_addr, size)
+ : memremap_sme_should_map_encrypted(phys_addr, size);
+}
+
/*
* Architecure function to determine if RAM remap is allowed.
*/
^ permalink raw reply related
* [RFC PATCH v2 30/32] kvm: svm: Add support for SEV DEBUG_ENCRYPT command
From: Brijesh Singh @ 2017-03-02 15:18 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
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 <brijesh.singh@amd.com>
---
arch/x86/kvm/svm.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c
index ce8819a..64899ed 100644
--- a/arch/x86/kvm/svm.c
+++ b/arch/x86/kvm/svm.c
@@ -6058,6 +6058,89 @@ static int sev_dbg_decrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
return ret;
}
+static int sev_dbg_encrypt(struct kvm *kvm, struct kvm_sev_cmd *argp)
+{
+ void *data;
+ int len, ret, d_off;
+ struct page **inpages;
+ struct kvm_sev_dbg debug;
+ struct sev_data_dbg *encrypt;
+ unsigned long src_addr, dst_addr, npages;
+
+ if (!sev_guest(kvm))
+ return -ENOTTY;
+
+ if (copy_from_user(&debug, argp, sizeof(*argp)))
+ return -EFAULT;
+
+ if (debug.length > PAGE_SIZE)
+ return -EINVAL;
+
+ len = debug.length;
+ src_addr = debug.src_addr;
+ dst_addr = debug.dst_addr;
+
+ inpages = sev_pin_memory(dst_addr, PAGE_SIZE, &npages);
+ if (!inpages)
+ return -EFAULT;
+
+ encrypt = kzalloc(sizeof(*encrypt), GFP_KERNEL);
+ if (!encrypt) {
+ ret = -ENOMEM;
+ goto err_1;
+ }
+
+ data = (void *) get_zeroed_page(GFP_KERNEL);
+ if (!data) {
+ ret = -ENOMEM;
+ goto err_2;
+ }
+
+ if ((len & 15) || (dst_addr & 15)) {
+ /* if destination address and length are not 16-byte
+ * aligned then:
+ * a) decrypt destination page into temporary buffer
+ * b) copy source data into temporary buffer at correct offset
+ * c) encrypt temporary buffer
+ */
+ ret = __sev_dbg_decrypt_page(kvm, dst_addr, data, &argp->error);
+ if (ret)
+ goto err_3;
+ d_off = dst_addr & (PAGE_SIZE - 1);
+
+ if (copy_from_user(data + d_off,
+ (uint8_t *)debug.src_addr, len)) {
+ ret = -EFAULT;
+ goto err_3;
+ }
+
+ encrypt->length = PAGE_SIZE;
+ encrypt->src_addr = __psp_pa(data);
+ encrypt->dst_addr = __sev_page_pa(inpages[0]);
+ } else {
+ if (copy_from_user(data, (uint8_t *)debug.src_addr, len)) {
+ ret = -EFAULT;
+ goto err_3;
+ }
+
+ d_off = dst_addr & (PAGE_SIZE - 1);
+ encrypt->length = len;
+ encrypt->src_addr = __psp_pa(data);
+ encrypt->dst_addr = __sev_page_pa(inpages[0]);
+ encrypt->dst_addr += d_off;
+ }
+
+ encrypt->handle = sev_get_handle(kvm);
+ ret = sev_issue_cmd(kvm, SEV_CMD_DBG_ENCRYPT, encrypt, &argp->error);
+err_3:
+ free_page((unsigned long)data);
+err_2:
+ kfree(encrypt);
+err_1:
+ sev_unpin_memory(inpages, npages);
+ return ret;
+}
+
static int amd_memory_encryption_cmd(struct kvm *kvm, void __user *argp)
{
int r = -ENOTTY;
@@ -6089,6 +6172,10 @@ static int amd_memory_encryption_cmd(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;
}
^ permalink raw reply related
* [RFC PATCH v2 09/32] x86: Change early_ioremap to early_memremap for BOOT data
From: Brijesh Singh @ 2017-03-02 15:13 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
In order to map BOOT data with the proper encryption bit, the
early_ioremap() function calls are changed to early_memremap() calls.
This allows the proper access for both SME and SEV.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
arch/x86/kernel/acpi/boot.c | 4 ++--
arch/x86/kernel/mpparse.c | 10 +++++-----
drivers/sfi/sfi_core.c | 6 +++---
3 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 35174c6..468c25a 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -124,7 +124,7 @@ char *__init __acpi_map_table(unsigned long phys, unsigned long size)
if (!phys || !size)
return NULL;
- return early_ioremap(phys, size);
+ return early_memremap(phys, size);
}
void __init __acpi_unmap_table(char *map, unsigned long size)
@@ -132,7 +132,7 @@ void __init __acpi_unmap_table(char *map, unsigned long size)
if (!map || !size)
return;
- early_iounmap(map, size);
+ early_memunmap(map, size);
}
#ifdef CONFIG_X86_LOCAL_APIC
diff --git a/arch/x86/kernel/mpparse.c b/arch/x86/kernel/mpparse.c
index 0d904d7..fd37f39 100644
--- a/arch/x86/kernel/mpparse.c
+++ b/arch/x86/kernel/mpparse.c
@@ -436,9 +436,9 @@ static unsigned long __init get_mpc_size(unsigned long physptr)
struct mpc_table *mpc;
unsigned long size;
- mpc = early_ioremap(physptr, PAGE_SIZE);
+ mpc = early_memremap(physptr, PAGE_SIZE);
size = mpc->length;
- early_iounmap(mpc, PAGE_SIZE);
+ early_memunmap(mpc, PAGE_SIZE);
apic_printk(APIC_VERBOSE, " mpc: %lx-%lx\n", physptr, physptr + size);
return size;
@@ -450,7 +450,7 @@ static int __init check_physptr(struct mpf_intel *mpf, unsigned int early)
unsigned long size;
size = get_mpc_size(mpf->physptr);
- mpc = early_ioremap(mpf->physptr, size);
+ mpc = early_memremap(mpf->physptr, size);
/*
* Read the physical hardware table. Anything here will
* override the defaults.
@@ -461,10 +461,10 @@ static int __init check_physptr(struct mpf_intel *mpf, unsigned int early)
#endif
pr_err("BIOS bug, MP table errors detected!...\n");
pr_cont("... disabling SMP support. (tell your hw vendor)\n");
- early_iounmap(mpc, size);
+ early_memunmap(mpc, size);
return -1;
}
- early_iounmap(mpc, size);
+ early_memunmap(mpc, size);
if (early)
return -1;
diff --git a/drivers/sfi/sfi_core.c b/drivers/sfi/sfi_core.c
index 296db7a..d00ae3f 100644
--- a/drivers/sfi/sfi_core.c
+++ b/drivers/sfi/sfi_core.c
@@ -92,7 +92,7 @@ static struct sfi_table_simple *syst_va __read_mostly;
static u32 sfi_use_ioremap __read_mostly;
/*
- * sfi_un/map_memory calls early_ioremap/iounmap which is a __init function
+ * sfi_un/map_memory calls early_memremap/memunmap which is a __init function
* and introduces section mismatch. So use __ref to make it calm.
*/
static void __iomem * __ref sfi_map_memory(u64 phys, u32 size)
@@ -103,7 +103,7 @@ static void __iomem * __ref sfi_map_memory(u64 phys, u32 size)
if (sfi_use_ioremap)
return ioremap_cache(phys, size);
else
- return early_ioremap(phys, size);
+ return early_memremap(phys, size);
}
static void __ref sfi_unmap_memory(void __iomem *virt, u32 size)
@@ -114,7 +114,7 @@ static void __ref sfi_unmap_memory(void __iomem *virt, u32 size)
if (sfi_use_ioremap)
iounmap(virt);
else
- early_iounmap(virt, size);
+ early_memunmap(virt, size);
}
static void sfi_print_table_header(unsigned long long pa,
^ permalink raw reply related
* [RFC PATCH v2 07/32] x86/efi: Access EFI data as encrypted when SEV is active
From: Brijesh Singh @ 2017-03-02 15:13 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
EFI data is encrypted when the kernel is run under SEV. Update the
page table references to be sure the EFI memory areas are accessed
encrypted.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
Signed-off-by: Brijesh Singh <brijesh.singh@amd.com>
---
arch/x86/platform/efi/efi_64.c | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/arch/x86/platform/efi/efi_64.c b/arch/x86/platform/efi/efi_64.c
index 2d8674d..9a76ed8 100644
--- a/arch/x86/platform/efi/efi_64.c
+++ b/arch/x86/platform/efi/efi_64.c
@@ -45,6 +45,7 @@
#include <asm/realmode.h>
#include <asm/time.h>
#include <asm/pgalloc.h>
+#include <asm/mem_encrypt.h>
/*
* We allocate runtime services regions bottom-up, starting from -4G, i.e.
@@ -286,7 +287,10 @@ int __init efi_setup_page_tables(unsigned long pa_memmap, unsigned num_pages)
* as trim_bios_range() will reserve the first page and isolate it away
* from memory allocators anyway.
*/
- if (kernel_map_pages_in_pgd(pgd, 0x0, 0x0, 1, _PAGE_RW)) {
+ pf = _PAGE_RW;
+ if (sev_active())
+ pf |= _PAGE_ENC;
+ if (kernel_map_pages_in_pgd(pgd, 0x0, 0x0, 1, pf)) {
pr_err("Failed to create 1:1 mapping for the first page!\n");
return 1;
}
@@ -329,6 +333,9 @@ static void __init __map_region(efi_memory_desc_t *md, u64 va)
if (!(md->attribute & EFI_MEMORY_WB))
flags |= _PAGE_PCD;
+ if (sev_active())
+ flags |= _PAGE_ENC;
+
pfn = md->phys_addr >> PAGE_SHIFT;
if (kernel_map_pages_in_pgd(pgd, pfn, va, md->num_pages, flags))
pr_warn("Error mapping PA 0x%llx -> VA 0x%llx!\n",
@@ -455,6 +462,9 @@ static int __init efi_update_mem_attr(struct mm_struct *mm, efi_memory_desc_t *m
if (!(md->attribute & EFI_MEMORY_RO))
pf |= _PAGE_RW;
+ if (sev_active())
+ pf |= _PAGE_ENC;
+
return efi_update_mappings(md, pf);
}
@@ -506,6 +516,9 @@ void __init efi_runtime_update_mappings(void)
(md->type != EFI_RUNTIME_SERVICES_CODE))
pf |= _PAGE_RW;
+ if (sev_active())
+ pf |= _PAGE_ENC;
+
efi_update_mappings(md, pf);
}
}
^ permalink raw reply related
* [RFC PATCH v2 08/32] x86: Use PAGE_KERNEL protection for ioremap of memory page
From: Brijesh Singh @ 2017-03-02 15:13 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
In order for memory pages to be properly mapped when SEV is active, we
need to use the PAGE_KERNEL protection attribute as the base protection.
This will insure that memory mapping of, e.g. ACPI tables, receives the
proper mapping attributes.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
arch/x86/mm/ioremap.c | 8 ++++++++
include/linux/mm.h | 1 +
kernel/resource.c | 40 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 49 insertions(+)
diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
index c400ab5..481c999 100644
--- a/arch/x86/mm/ioremap.c
+++ b/arch/x86/mm/ioremap.c
@@ -151,7 +151,15 @@ static void __iomem *__ioremap_caller(resource_size_t phys_addr,
pcm = new_pcm;
}
+ /*
+ * If the page being mapped is in memory and SEV is active then
+ * make sure the memory encryption attribute is enabled in the
+ * resulting mapping.
+ */
prot = PAGE_KERNEL_IO;
+ if (sev_active() && page_is_mem(pfn))
+ prot = __pgprot(pgprot_val(prot) | _PAGE_ENC);
+
switch (pcm) {
case _PAGE_CACHE_MODE_UC:
default:
diff --git a/include/linux/mm.h b/include/linux/mm.h
index b84615b..825df27 100644
--- a/include/linux/mm.h
+++ b/include/linux/mm.h
@@ -445,6 +445,7 @@ static inline int get_page_unless_zero(struct page *page)
}
extern int page_is_ram(unsigned long pfn);
+extern int page_is_mem(unsigned long pfn);
enum {
REGION_INTERSECTS,
diff --git a/kernel/resource.c b/kernel/resource.c
index 9b5f044..db56ba3 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -518,6 +518,46 @@ int __weak page_is_ram(unsigned long pfn)
}
EXPORT_SYMBOL_GPL(page_is_ram);
+/*
+ * This function returns true if the target memory is marked as
+ * IORESOURCE_MEM and IORESOUCE_BUSY and described as other than
+ * IORES_DESC_NONE (e.g. IORES_DESC_ACPI_TABLES).
+ */
+static int walk_mem_range(unsigned long start_pfn, unsigned long nr_pages)
+{
+ struct resource res;
+ unsigned long pfn, end_pfn;
+ u64 orig_end;
+ int ret = -1;
+
+ res.start = (u64) start_pfn << PAGE_SHIFT;
+ res.end = ((u64)(start_pfn + nr_pages) << PAGE_SHIFT) - 1;
+ res.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+ orig_end = res.end;
+ while ((res.start < res.end) &&
+ (find_next_iomem_res(&res, IORES_DESC_NONE, true) >= 0)) {
+ pfn = (res.start + PAGE_SIZE - 1) >> PAGE_SHIFT;
+ end_pfn = (res.end + 1) >> PAGE_SHIFT;
+ if (end_pfn > pfn)
+ ret = (res.desc != IORES_DESC_NONE) ? 1 : 0;
+ if (ret)
+ break;
+ res.start = res.end + 1;
+ res.end = orig_end;
+ }
+ return ret;
+}
+
+/*
+ * This generic page_is_mem() returns true if specified address is
+ * registered as memory in iomem_resource list.
+ */
+int __weak page_is_mem(unsigned long pfn)
+{
+ return walk_mem_range(pfn, 1) == 1;
+}
+EXPORT_SYMBOL_GPL(page_is_mem);
+
/**
* region_intersects() - determine intersection of region with known resources
* @start: region start address
^ permalink raw reply related
* [RFC PATCH v2 06/32] x86/pci: Use memremap when walking setup data
From: Brijesh Singh @ 2017-03-02 15:13 UTC (permalink / raw)
To: simon.guinot, linux-efi, brijesh.singh, kvm, rkrcmar, matt,
linux-pci, linus.walleij, gary.hook, linux-mm, paul.gortmaker,
hpa, cl, dan.j.williams, aarcange, sfr, andriy.shevchenko,
herbert, bhe, xemul, joro, x86, peterz, piotr.luc, mingo, msalter,
ross.zwisler, bp, dyoung, thomas.lendacky, jroedel, keescook,
arnd, <toshi
In-Reply-To: <148846752022.2349.13667498174822419498.stgit@brijesh-build-machine>
From: Tom Lendacky <thomas.lendacky@amd.com>
The use of ioremap will force the setup data to be mapped decrypted even
though setup data is encrypted. Switch to using memremap which will be
able to perform the proper mapping.
Signed-off-by: Tom Lendacky <thomas.lendacky@amd.com>
---
arch/x86/pci/common.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
index a4fdfa7..0b06670 100644
--- a/arch/x86/pci/common.c
+++ b/arch/x86/pci/common.c
@@ -691,7 +691,7 @@ int pcibios_add_device(struct pci_dev *dev)
pa_data = boot_params.hdr.setup_data;
while (pa_data) {
- data = ioremap(pa_data, sizeof(*rom));
+ data = memremap(pa_data, sizeof(*rom), MEMREMAP_WB);
if (!data)
return -ENOMEM;
@@ -710,7 +710,7 @@ int pcibios_add_device(struct pci_dev *dev)
}
}
pa_data = data->next;
- iounmap(data);
+ memunmap(data);
}
set_dma_domain_ops(dev);
set_dev_domain_options(dev);
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox