KVM: VMX: Add emulation of SGX Launch Control LE hash MSRs
authorSean Christopherson <sean.j.christopherson@intel.com>
Mon, 12 Apr 2021 04:21:40 +0000 (16:21 +1200)
committerPaolo Bonzini <pbonzini@redhat.com>
Tue, 20 Apr 2021 08:18:55 +0000 (04:18 -0400)
Emulate the four Launch Enclave public key hash MSRs (LE hash MSRs) that
exist on CPUs that support SGX Launch Control (LC).  SGX LC modifies the
behavior of ENCLS[EINIT] to use the LE hash MSRs when verifying the key
used to sign an enclave.  On CPUs without LC support, the LE hash is
hardwired into the CPU to an Intel controlled key (the Intel key is also
the reset value of the LE hash MSRs). Track the guest's desired hash so
that a future patch can stuff the hash into the hardware MSRs when
executing EINIT on behalf of the guest, when those MSRs are writable in
host.

Signed-off-by: Sean Christopherson <sean.j.christopherson@intel.com>
Co-developed-by: Kai Huang <kai.huang@intel.com>
Signed-off-by: Kai Huang <kai.huang@intel.com>
Message-Id: <c58ef601ddf88f3a113add837969533099b1364a.1618196135.git.kai.huang@intel.com>
[Add a comment regarding the MSRs being available until SGX is locked.
 - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
arch/x86/kvm/vmx/sgx.c
arch/x86/kvm/vmx/sgx.h
arch/x86/kvm/vmx/vmx.c
arch/x86/kvm/vmx/vmx.h

index 2cf9d7c..d53e6b1 100644 (file)
@@ -11,6 +11,9 @@
 
 bool __read_mostly enable_sgx;
 
+/* Initial value of guest's virtual SGX_LEPUBKEYHASHn MSRs */
+static u64 sgx_pubkey_hash[4] __ro_after_init;
+
 /*
  * ENCLS's memory operands use a fixed segment (DS) and a fixed
  * address size based on the mode.  Related prefixes are ignored.
@@ -323,3 +326,35 @@ int handle_encls(struct kvm_vcpu *vcpu)
        }
        return 1;
 }
+
+void setup_default_sgx_lepubkeyhash(void)
+{
+       /*
+        * Use Intel's default value for Skylake hardware if Launch Control is
+        * not supported, i.e. Intel's hash is hardcoded into silicon, or if
+        * Launch Control is supported and enabled, i.e. mimic the reset value
+        * and let the guest write the MSRs at will.  If Launch Control is
+        * supported but disabled, then use the current MSR values as the hash
+        * MSRs exist but are read-only (locked and not writable).
+        */
+       if (!enable_sgx || boot_cpu_has(X86_FEATURE_SGX_LC) ||
+           rdmsrl_safe(MSR_IA32_SGXLEPUBKEYHASH0, &sgx_pubkey_hash[0])) {
+               sgx_pubkey_hash[0] = 0xa6053e051270b7acULL;
+               sgx_pubkey_hash[1] = 0x6cfbe8ba8b3b413dULL;
+               sgx_pubkey_hash[2] = 0xc4916d99f2b3735dULL;
+               sgx_pubkey_hash[3] = 0xd4f8c05909f9bb3bULL;
+       } else {
+               /* MSR_IA32_SGXLEPUBKEYHASH0 is read above */
+               rdmsrl(MSR_IA32_SGXLEPUBKEYHASH1, sgx_pubkey_hash[1]);
+               rdmsrl(MSR_IA32_SGXLEPUBKEYHASH2, sgx_pubkey_hash[2]);
+               rdmsrl(MSR_IA32_SGXLEPUBKEYHASH3, sgx_pubkey_hash[3]);
+       }
+}
+
+void vcpu_setup_sgx_lepubkeyhash(struct kvm_vcpu *vcpu)
+{
+       struct vcpu_vmx *vmx = to_vmx(vcpu);
+
+       memcpy(vmx->msr_ia32_sgxlepubkeyhash, sgx_pubkey_hash,
+              sizeof(sgx_pubkey_hash));
+}
index 6e17ecd..6502fa5 100644 (file)
@@ -8,8 +8,14 @@
 extern bool __read_mostly enable_sgx;
 
 int handle_encls(struct kvm_vcpu *vcpu);
+
+void setup_default_sgx_lepubkeyhash(void);
+void vcpu_setup_sgx_lepubkeyhash(struct kvm_vcpu *vcpu);
 #else
 #define enable_sgx 0
+
+static inline void setup_default_sgx_lepubkeyhash(void) { }
+static inline void vcpu_setup_sgx_lepubkeyhash(struct kvm_vcpu *vcpu) { }
 #endif
 
 #endif /* __KVM_X86_SGX_H */
index d210364..237a3f2 100644 (file)
@@ -1922,6 +1922,13 @@ static int vmx_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
        case MSR_IA32_FEAT_CTL:
                msr_info->data = vmx->msr_ia32_feature_control;
                break;
+       case MSR_IA32_SGXLEPUBKEYHASH0 ... MSR_IA32_SGXLEPUBKEYHASH3:
+               if (!msr_info->host_initiated &&
+                   !guest_cpuid_has(vcpu, X86_FEATURE_SGX_LC))
+                       return 1;
+               msr_info->data = to_vmx(vcpu)->msr_ia32_sgxlepubkeyhash
+                       [msr_info->index - MSR_IA32_SGXLEPUBKEYHASH0];
+               break;
        case MSR_IA32_VMX_BASIC ... MSR_IA32_VMX_VMFUNC:
                if (!nested_vmx_allowed(vcpu))
                        return 1;
@@ -2216,6 +2223,26 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info)
                if (msr_info->host_initiated && data == 0)
                        vmx_leave_nested(vcpu);
                break;
+       case MSR_IA32_SGXLEPUBKEYHASH0 ... MSR_IA32_SGXLEPUBKEYHASH3:
+               /*
+                * On real hardware, the LE hash MSRs are writable before
+                * the firmware sets bit 0 in MSR 0x7a ("activating" SGX),
+                * at which point SGX related bits in IA32_FEATURE_CONTROL
+                * become writable.
+                *
+                * KVM does not emulate SGX activation for simplicity, so
+                * allow writes to the LE hash MSRs if IA32_FEATURE_CONTROL
+                * is unlocked.  This is technically not architectural
+                * behavior, but it's close enough.
+                */
+               if (!msr_info->host_initiated &&
+                   (!guest_cpuid_has(vcpu, X86_FEATURE_SGX_LC) ||
+                   ((vmx->msr_ia32_feature_control & FEAT_CTL_LOCKED) &&
+                   !(vmx->msr_ia32_feature_control & FEAT_CTL_SGX_LC_ENABLED))))
+                       return 1;
+               vmx->msr_ia32_sgxlepubkeyhash
+                       [msr_index - MSR_IA32_SGXLEPUBKEYHASH0] = data;
+               break;
        case MSR_IA32_VMX_BASIC ... MSR_IA32_VMX_VMFUNC:
                if (!msr_info->host_initiated)
                        return 1; /* they are read-only */
@@ -6978,6 +7005,8 @@ static int vmx_create_vcpu(struct kvm_vcpu *vcpu)
        else
                memset(&vmx->nested.msrs, 0, sizeof(vmx->nested.msrs));
 
+       vcpu_setup_sgx_lepubkeyhash(vcpu);
+
        vmx->nested.posted_intr_nv = -1;
        vmx->nested.current_vmptr = -1ull;
 
@@ -7915,6 +7944,8 @@ static __init int hardware_setup(void)
        if (!enable_ept || !cpu_has_vmx_intel_pt())
                pt_mode = PT_MODE_SYSTEM;
 
+       setup_default_sgx_lepubkeyhash();
+
        if (nested) {
                nested_vmx_setup_ctls_msrs(&vmcs_config.nested,
                                           vmx_capability.ept);
index 7886a08..19fe09f 100644 (file)
@@ -325,6 +325,9 @@ struct vcpu_vmx {
         */
        u64 msr_ia32_feature_control;
        u64 msr_ia32_feature_control_valid_bits;
+       /* SGX Launch Control public key hash */
+       u64 msr_ia32_sgxlepubkeyhash[4];
+
 #if IS_ENABLED(CONFIG_HYPERV)
        u64 hv_root_ept;
 #endif