@ -179,6 +179,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext)
case KVM_CAP_MP_STATE :
case KVM_CAP_S390_USER_SIGP :
case KVM_CAP_S390_USER_STSI :
case KVM_CAP_S390_SKEYS :
r = 1 ;
break ;
case KVM_CAP_S390_MEM_OP :
@ -729,6 +730,108 @@ static int kvm_s390_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr)
return ret ;
}
static long kvm_s390_get_skeys ( struct kvm * kvm , struct kvm_s390_skeys * args )
{
uint8_t * keys ;
uint64_t hva ;
unsigned long curkey ;
int i , r = 0 ;
if ( args - > flags ! = 0 )
return - EINVAL ;
/* Is this guest using storage keys? */
if ( ! mm_use_skey ( current - > mm ) )
return KVM_S390_GET_SKEYS_NONE ;
/* Enforce sane limit on memory allocation */
if ( args - > count < 1 | | args - > count > KVM_S390_SKEYS_MAX )
return - EINVAL ;
keys = kmalloc_array ( args - > count , sizeof ( uint8_t ) ,
GFP_KERNEL | __GFP_NOWARN ) ;
if ( ! keys )
keys = vmalloc ( sizeof ( uint8_t ) * args - > count ) ;
if ( ! keys )
return - ENOMEM ;
for ( i = 0 ; i < args - > count ; i + + ) {
hva = gfn_to_hva ( kvm , args - > start_gfn + i ) ;
if ( kvm_is_error_hva ( hva ) ) {
r = - EFAULT ;
goto out ;
}
curkey = get_guest_storage_key ( current - > mm , hva ) ;
if ( IS_ERR_VALUE ( curkey ) ) {
r = curkey ;
goto out ;
}
keys [ i ] = curkey ;
}
r = copy_to_user ( ( uint8_t __user * ) args - > skeydata_addr , keys ,
sizeof ( uint8_t ) * args - > count ) ;
if ( r )
r = - EFAULT ;
out :
kvfree ( keys ) ;
return r ;
}
static long kvm_s390_set_skeys ( struct kvm * kvm , struct kvm_s390_skeys * args )
{
uint8_t * keys ;
uint64_t hva ;
int i , r = 0 ;
if ( args - > flags ! = 0 )
return - EINVAL ;
/* Enforce sane limit on memory allocation */
if ( args - > count < 1 | | args - > count > KVM_S390_SKEYS_MAX )
return - EINVAL ;
keys = kmalloc_array ( args - > count , sizeof ( uint8_t ) ,
GFP_KERNEL | __GFP_NOWARN ) ;
if ( ! keys )
keys = vmalloc ( sizeof ( uint8_t ) * args - > count ) ;
if ( ! keys )
return - ENOMEM ;
r = copy_from_user ( keys , ( uint8_t __user * ) args - > skeydata_addr ,
sizeof ( uint8_t ) * args - > count ) ;
if ( r ) {
r = - EFAULT ;
goto out ;
}
/* Enable storage key handling for the guest */
s390_enable_skey ( ) ;
for ( i = 0 ; i < args - > count ; i + + ) {
hva = gfn_to_hva ( kvm , args - > start_gfn + i ) ;
if ( kvm_is_error_hva ( hva ) ) {
r = - EFAULT ;
goto out ;
}
/* Lowest order bit is reserved */
if ( keys [ i ] & 0x01 ) {
r = - EINVAL ;
goto out ;
}
r = set_guest_storage_key ( current - > mm , hva ,
( unsigned long ) keys [ i ] , 0 ) ;
if ( r )
goto out ;
}
out :
kvfree ( keys ) ;
return r ;
}
long kvm_arch_vm_ioctl ( struct file * filp ,
unsigned int ioctl , unsigned long arg )
{
@ -788,6 +891,26 @@ long kvm_arch_vm_ioctl(struct file *filp,
r = kvm_s390_vm_has_attr ( kvm , & attr ) ;
break ;
}
case KVM_S390_GET_SKEYS : {
struct kvm_s390_skeys args ;
r = - EFAULT ;
if ( copy_from_user ( & args , argp ,
sizeof ( struct kvm_s390_skeys ) ) )
break ;
r = kvm_s390_get_skeys ( kvm , & args ) ;
break ;
}
case KVM_S390_SET_SKEYS : {
struct kvm_s390_skeys args ;
r = - EFAULT ;
if ( copy_from_user ( & args , argp ,
sizeof ( struct kvm_s390_skeys ) ) )
break ;
r = kvm_s390_set_skeys ( kvm , & args ) ;
break ;
}
default :
r = - ENOTTY ;
}