@ -118,6 +118,8 @@ struct SevCommonState {
uint32_t cbitpos ;
uint32_t reduced_phys_bits ;
bool kernel_hashes ;
uint64_t sev_features ;
uint64_t supported_sev_features ;
/* runtime state */
uint8_t api_major ;
@ -485,7 +487,40 @@ static void sev_apply_cpu_context(CPUState *cpu)
}
}
static int check_vmsa_supported ( hwaddr gpa , const struct sev_es_save_area * vmsa ,
static int check_sev_features ( SevCommonState * sev_common , uint64_t sev_features ,
Error * * errp )
{
/*
* Ensure SEV_FEATURES is configured for correct SEV hardware and that
* the requested features are supported . If SEV - SNP is enabled then
* that feature must be enabled , otherwise it must be cleared .
*/
if ( sev_snp_enabled ( ) & & ! ( sev_features & SVM_SEV_FEAT_SNP_ACTIVE ) ) {
error_setg (
errp ,
" %s: SEV_SNP is enabled but is not enabled in VMSA sev_features " ,
__func__ ) ;
return - 1 ;
} else if ( ! sev_snp_enabled ( ) & &
( sev_features & SVM_SEV_FEAT_SNP_ACTIVE ) ) {
error_setg (
errp ,
" %s: SEV_SNP is not enabled but is enabled in VMSA sev_features " ,
__func__ ) ;
return - 1 ;
}
if ( sev_features & ~ sev_common - > supported_sev_features ) {
error_setg ( errp ,
" %s: VMSA contains unsupported sev_features: %lX, "
" supported features: %lX " ,
__func__ , sev_features , sev_common - > supported_sev_features ) ;
return - 1 ;
}
return 0 ;
}
static int check_vmsa_supported ( SevCommonState * sev_common , hwaddr gpa ,
const struct sev_es_save_area * vmsa ,
Error * * errp )
{
struct sev_es_save_area vmsa_check ;
@ -551,24 +586,10 @@ static int check_vmsa_supported(hwaddr gpa, const struct sev_es_save_area *vmsa,
vmsa_check . x87_fcw = 0 ;
vmsa_check . mxcsr = 0 ;
if ( sev_snp_enabled ( ) ) {
if ( vmsa_check . sev_features ! = SVM_SEV_FEAT_SNP_ACTIVE ) {
error_setg ( errp ,
" %s: sev_features in the VMSA contains an unsupported "
" value. For SEV-SNP, sev_features must be set to %x. " ,
__func__ , SVM_SEV_FEAT_SNP_ACTIVE ) ;
return - 1 ;
}
vmsa_check . sev_features = 0 ;
} else {
if ( vmsa_check . sev_features ! = 0 ) {
error_setg ( errp ,
" %s: sev_features in the VMSA contains an unsupported "
" value. For SEV-ES and SEV, sev_features must be "
" set to 0. " , __func__ ) ;
return - 1 ;
}
if ( check_sev_features ( sev_common , vmsa_check . sev_features , errp ) < 0 ) {
return - 1 ;
}
vmsa_check . sev_features = 0 ;
if ( ! buffer_is_zero ( & vmsa_check , sizeof ( vmsa_check ) ) ) {
error_setg ( errp ,
@ -1722,6 +1743,39 @@ static int sev_snp_kvm_type(X86ConfidentialGuest *cg)
return KVM_X86_SNP_VM ;
}
static int sev_init_supported_features ( ConfidentialGuestSupport * cgs ,
SevCommonState * sev_common , Error * * errp )
{
X86ConfidentialGuestClass * x86_klass =
X86_CONFIDENTIAL_GUEST_GET_CLASS ( cgs ) ;
/*
* Older kernels do not support query or setting of sev_features . In this
* case the set of supported features must be zero to match the settings
* in the kernel .
*/
if ( x86_klass - > kvm_type ( X86_CONFIDENTIAL_GUEST ( sev_common ) ) = =
KVM_X86_DEFAULT_VM ) {
sev_common - > supported_sev_features = 0 ;
return 0 ;
}
/* Query KVM for the supported set of sev_features */
struct kvm_device_attr attr = {
. group = KVM_X86_GRP_SEV ,
. attr = KVM_X86_SEV_VMSA_FEATURES ,
. addr = ( unsigned long ) & sev_common - > supported_sev_features ,
} ;
if ( kvm_ioctl ( kvm_state , KVM_GET_DEVICE_ATTR , & attr ) < 0 ) {
error_setg ( errp , " %s: failed to query supported sev_features " ,
__func__ ) ;
return - 1 ;
}
if ( sev_snp_enabled ( ) ) {
sev_common - > supported_sev_features | = SVM_SEV_FEAT_SNP_ACTIVE ;
}
return 0 ;
}
static int sev_common_kvm_init ( ConfidentialGuestSupport * cgs , Error * * errp )
{
char * devname ;
@ -1802,6 +1856,10 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
}
}
if ( sev_init_supported_features ( cgs , sev_common , errp ) < 0 ) {
return - 1 ;
}
trace_kvm_sev_init ( ) ;
switch ( x86_klass - > kvm_type ( X86_CONFIDENTIAL_GUEST ( sev_common ) ) ) {
case KVM_X86_DEFAULT_VM :
@ -1813,6 +1871,40 @@ static int sev_common_kvm_init(ConfidentialGuestSupport *cgs, Error **errp)
case KVM_X86_SEV_ES_VM :
case KVM_X86_SNP_VM : {
struct kvm_sev_init args = { 0 } ;
MachineState * machine = MACHINE ( qdev_get_machine ( ) ) ;
X86MachineState * x86machine = X86_MACHINE ( qdev_get_machine ( ) ) ;
/*
* If configuration is provided via an IGVM file then the IGVM file
* might contain configuration of the initial vcpu context . For SEV
* the vcpu context includes the sev_features which should be applied
* to the vcpu .
*
* KVM does not synchronize sev_features from CPU state . Instead it
* requires sev_features to be provided as part of this initialization
* call which is subsequently automatically applied to the VMSA of
* each vcpu .
*
* The IGVM file is normally processed after initialization . Therefore
* we need to pre - process it here to extract sev_features in order to
* provide it to KVM_SEV_INIT2 . Each cgs_ * function that is called by
* the IGVM processor detects this pre - process by observing the state
* as SEV_STATE_UNINIT .
*/
if ( x86machine - > igvm ) {
if ( IGVM_CFG_GET_CLASS ( x86machine - > igvm )
- > process ( x86machine - > igvm , machine - > cgs , true , errp ) = =
- 1 ) {
return - 1 ;
}
/*
* KVM maintains a bitmask of allowed sev_features . This does not
* include SVM_SEV_FEAT_SNP_ACTIVE which is set accordingly by KVM
* itself . Therefore we need to clear this flag .
*/
args . vmsa_features = sev_common - > sev_features &
~ SVM_SEV_FEAT_SNP_ACTIVE ;
}
ret = sev_ioctl ( sev_common - > sev_fd , KVM_SEV_INIT2 , & args , & fw_error ) ;
break ;
@ -2416,6 +2508,24 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
SevCommonState * sev_common = SEV_COMMON ( MACHINE ( qdev_get_machine ( ) ) - > cgs ) ;
SevCommonStateClass * klass = SEV_COMMON_GET_CLASS ( sev_common ) ;
if ( sev_common - > state = = SEV_STATE_UNINIT ) {
/* Pre-processing of IGVM file called from sev_common_kvm_init() */
if ( ( cpu_index = = 0 ) & & ( memory_type = = CGS_PAGE_TYPE_VMSA ) ) {
const struct sev_es_save_area * sa =
( const struct sev_es_save_area * ) ptr ;
if ( len < sizeof ( * sa ) ) {
error_setg ( errp , " %s: invalid VMSA length encountered " ,
__func__ ) ;
return - 1 ;
}
if ( check_sev_features ( sev_common , sa - > sev_features , errp ) < 0 ) {
return - 1 ;
}
sev_common - > sev_features = sa - > sev_features ;
}
return 0 ;
}
if ( ! sev_enabled ( ) ) {
error_setg ( errp , " %s: attempt to configure guest memory, but SEV "
" is not enabled " , __func__ ) ;
@ -2435,7 +2545,8 @@ static int cgs_set_guest_state(hwaddr gpa, uint8_t *ptr, uint64_t len,
__func__ ) ;
return - 1 ;
}
if ( check_vmsa_supported ( gpa , ( const struct sev_es_save_area * ) ptr ,
if ( check_vmsa_supported ( sev_common , gpa ,
( const struct sev_es_save_area * ) ptr ,
errp ) < 0 ) {
return - 1 ;
}
@ -2492,6 +2603,12 @@ static int cgs_get_mem_map_entry(int index,
struct e820_entry * table ;
int num_entries ;
SevCommonState * sev_common = SEV_COMMON ( MACHINE ( qdev_get_machine ( ) ) - > cgs ) ;
if ( sev_common - > state = = SEV_STATE_UNINIT ) {
/* Pre-processing of IGVM file called from sev_common_kvm_init() */
return 1 ;
}
num_entries = e820_get_table ( & table ) ;
if ( ( index < 0 ) | | ( index > = num_entries ) ) {
return 1 ;
@ -2523,6 +2640,12 @@ static int cgs_set_guest_policy(ConfidentialGuestPolicyType policy_type,
uint32_t policy_data1_size , void * policy_data2 ,
uint32_t policy_data2_size , Error * * errp )
{
SevCommonState * sev_common = SEV_COMMON ( MACHINE ( qdev_get_machine ( ) ) - > cgs ) ;
if ( sev_common - > state = = SEV_STATE_UNINIT ) {
/* Pre-processing of IGVM file called from sev_common_kvm_init() */
return 0 ;
}
if ( policy_type ! = GUEST_POLICY_SEV ) {
error_setg ( errp , " %s: Invalid guest policy type provided for SEV: %d " ,
__func__ , policy_type ) ;