hv: keylocker: Support Key Locker feature for guest VM

KeyLocker is a new security feature available in new Intel CPUs that
protects data-encryption keys for the Advanced Encryption Standard (AES)
algorithm. These keys are more valuable than what they guard. If stolen
once, the key can be repeatedly used even on another system and even
after vulnerability closed.

It also introduces a CPU-internal wrapping key (IWKey), which is a key-
encryption key to wrap AES keys into handles. While the IWKey is
inaccessible to software, randomizing the value during the boot-time
helps its value unpredictable.

Keylocker usage:
 - New “ENCODEKEY” instructions take original key input and returns HANDLE
   crypted by an internal wrap key (IWKey, init by “LOADIWKEY” instruction)
 - Software can then delete the original key from memory
 - Early in boot/software, less likely to have vulnerability that allows
   stealing original key
 - Later encrypt/decrypt can use the HANDLE through new AES KeyLocker
   instructions
 - Note:
      * Software can use original key without knowing it (use HANDLE)
      * HANDLE cannot be used on other systems or after warm/cold reset
      * IWKey cannot be read from CPU after it's loaded (this is the
        nature of this feature) and only 1 copy of IWKey inside CPU.

The virtualization implementation of Key Locker on ACRN is:
 - Each vCPU has a 'struct iwkey' to store its IWKey in struct
   acrn_vcpu_arch.
 - At initilization, every vCPU is created with a random IWKey.
 - Hypervisor traps the execution of LOADIWKEY (by 'LOADIWKEY exiting'
   VM-exectuion control) of vCPU to capture and save the IWKey if guest
   set a new IWKey. Don't support randomization (emulate CPUID to
   disable) of the LOADIWKEY as hypervisor cannot capture and save the
   random IWKey. From keylocker spec:
   "Note that a VMM may wish to enumerate no support for HW random IWKeys
   to the guest (i.e. enumerate CPUID.19H:ECX[1] as 0) as such IWKeys
   cannot be easily context switched. A guest ENCODEKEY will return the
   type of IWKey used (IWKey.KeySource) and thus will notice if a VMM
   virtualized a HW random IWKey with a SW specified IWKey."
 - In context_switch_in() of each vCPU, hypervisor loads that vCPU's
   IWKey into pCPU by LOADIWKEY instruction.
 - There is an assumption that ACRN hypervisor will never use the
   KeyLocker feature itself.

This patch implements the vCPU's IWKey management and the next patch
implements host context save/restore IWKey logic.

Tracked-On: #5695
Signed-off-by: Shuo A Liu <shuo.a.liu@intel.com>
Acked-by: Eddie Dong <eddie.dong@intel.com>
This commit is contained in:
Shuo A Liu
2020-08-24 20:20:43 +08:00
committed by wenlingz
parent 4483e93bd1
commit c11c07e0fe
10 changed files with 102 additions and 5 deletions

View File

@@ -659,6 +659,11 @@ static inline void xrstors(const struct xsave_area *region_addr, uint64_t mask)
"memory");
}
static inline void loadiwkey(uint32_t eax)
{
asm volatile(".byte 0xf3, 0x0f, 0x38, 0xdc, 0xd1;": : "a" (eax));
}
/*
* stac/clac pair is used to access guest's memory protected by SMAP,
* following below flow:

View File

@@ -35,6 +35,7 @@
#define X86_FEATURE_XSAVE ((FEAT_1_ECX << 5U) + 26U)
#define X86_FEATURE_OSXSAVE ((FEAT_1_ECX << 5U) + 27U)
#define X86_FEATURE_AVX ((FEAT_1_ECX << 5U) + 28U)
#define X86_FEATURE_RDRAND ((FEAT_1_ECX << 5U) + 30U)
/* Intel-defined CPU features, CPUID level 0x00000001 (EDX)*/
#define X86_FEATURE_FPU ((FEAT_1_EDX << 5U) + 0U)

View File

@@ -203,6 +203,13 @@ struct msr_store_area {
uint32_t count; /* actual count of entries to be loaded/restored during VMEntry/VMExit */
};
struct iwkey {
/* 256bit encryption key */
uint64_t encryption_key[4];
/* 128bit integration key */
uint64_t integrity_key[2];
};
struct acrn_vcpu_arch {
/* vmcs region for this vcpu, MUST be 4KB-aligned */
uint8_t vmcs[PAGE_SIZE];
@@ -261,6 +268,7 @@ struct acrn_vcpu_arch {
uint64_t eoi_exit_bitmap[EOI_EXIT_BITMAP_SIZE >> 6U];
/* Keylocker */
struct iwkey IWKey;
bool cr4_kl_enabled;
} __aligned(PAGE_SIZE);

View File

@@ -22,6 +22,7 @@ void cpu_l1d_flush(void);
bool check_cpu_security_cap(void);
void cpu_internal_buffers_clear(void);
bool is_ept_force_4k_ipage(void);
uint64_t get_random_value(void);
#ifdef STACK_PROTECTOR
struct stack_canary {

View File

@@ -64,6 +64,10 @@
#define VMX_XSS_EXITING_BITMAP_FULL 0x0000202CU
#define VMX_XSS_EXITING_BITMAP_HIGH 0x0000202DU
#define VMX_PROC_VM_EXEC_CONTROLS3_FULL 0x00002034U
#define VMX_PROC_VM_EXEC_CONTROLS3_HIGH 0x00002035U
/* 64-bit read-only data fields */
#define VMX_GUEST_PHYSICAL_ADDR_FULL 0x00002400U
#define VMX_GUEST_PHYSICAL_ADDR_HIGH 0x00002401U
@@ -263,6 +267,7 @@
#define VMX_EXIT_REASON_PAGE_MODIFICATION_LOG_FULL 0x0000003EU
#define VMX_EXIT_REASON_XSAVES 0x0000003FU
#define VMX_EXIT_REASON_XRSTORS 0x00000040U
#define VMX_EXIT_REASON_LOADIWKEY 0x00000045U
/* VMX execution control bits (pin based) */
#define VMX_PINBASED_CTLS_IRQ_EXIT (1U<<0U)
@@ -312,6 +317,7 @@
#define VMX_PROCBASED_CTLS2_RDSEED (1U<<16U)
#define VMX_PROCBASED_CTLS2_EPT_VE (1U<<18U)
#define VMX_PROCBASED_CTLS2_XSVE_XRSTR (1U<<20U)
#define VMX_PROCBASED_CTLS3_LOADIWKEY (1U<<0U)
/* MSR_IA32_VMX_EPT_VPID_CAP: EPT and VPID capability bits */
#define VMX_EPT_EXECUTE_ONLY (1U << 0U)