2013-01-21 03:28:13 +04:00
/*
* Copyright ( C ) 2012 - ARM Ltd
* Author : Marc Zyngier < marc . zyngier @ arm . 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2014-10-16 19:00:18 +04:00
# include <linux/preempt.h>
2013-01-21 03:28:13 +04:00
# include <linux/kvm_host.h>
# include <linux/wait.h>
2013-10-18 21:19:03 +04:00
# include <asm/cputype.h>
2013-01-21 03:28:13 +04:00
# include <asm/kvm_emulate.h>
# include <asm/kvm_psci.h>
2014-06-02 17:37:13 +04:00
# include <asm/kvm_host.h>
2013-01-21 03:28:13 +04:00
/*
* This is an implementation of the Power State Coordination Interface
* as described in ARM document number ARM DEN 0022 A .
*/
2014-04-29 09:54:21 +04:00
# define AFFINITY_MASK(level) ~((0x1UL << ((level) * MPIDR_LEVEL_BITS)) - 1)
static unsigned long psci_affinity_mask ( unsigned long affinity_level )
{
if ( affinity_level < = 3 )
return MPIDR_HWID_BITMASK & AFFINITY_MASK ( affinity_level ) ;
return 0 ;
}
2014-04-29 09:54:24 +04:00
static unsigned long kvm_psci_vcpu_suspend ( struct kvm_vcpu * vcpu )
{
/*
* NOTE : For simplicity , we make VCPU suspend emulation to be
* same - as WFI ( Wait - for - interrupt ) emulation .
*
* This means for KVM the wakeup events are interrupts and
* this is consistent with intended use of StateID as described
* in section 5.4 .1 of PSCI v0 .2 specification ( ARM DEN 0022 A ) .
*
* Further , we also treat power - down request to be same as
* stand - by request as - per section 5.4 .2 clause 3 of PSCI v0 .2
* specification ( ARM DEN 0022 A ) . This means all suspend states
* for KVM will preserve the register state .
*/
kvm_vcpu_block ( vcpu ) ;
return PSCI_RET_SUCCESS ;
}
2013-01-21 03:28:13 +04:00
static void kvm_psci_vcpu_off ( struct kvm_vcpu * vcpu )
{
vcpu - > arch . pause = true ;
}
static unsigned long kvm_psci_vcpu_on ( struct kvm_vcpu * source_vcpu )
{
struct kvm * kvm = source_vcpu - > kvm ;
2014-06-02 17:37:13 +04:00
struct kvm_vcpu * vcpu = NULL ;
2013-01-21 03:28:13 +04:00
wait_queue_head_t * wq ;
unsigned long cpu_id ;
2014-04-29 09:54:23 +04:00
unsigned long context_id ;
2013-01-21 03:28:13 +04:00
phys_addr_t target_pc ;
2014-06-02 17:37:13 +04:00
cpu_id = * vcpu_reg ( source_vcpu , 1 ) & MPIDR_HWID_BITMASK ;
2013-01-21 03:28:13 +04:00
if ( vcpu_mode_is_32bit ( source_vcpu ) )
cpu_id & = ~ ( ( u32 ) 0 ) ;
2014-06-02 17:37:13 +04:00
vcpu = kvm_mpidr_to_vcpu ( kvm , cpu_id ) ;
2013-10-18 21:19:03 +04:00
2013-11-20 05:43:19 +04:00
/*
* Make sure the caller requested a valid CPU and that the CPU is
* turned off .
*/
2014-04-29 09:54:23 +04:00
if ( ! vcpu )
2014-04-29 09:54:16 +04:00
return PSCI_RET_INVALID_PARAMS ;
2014-04-29 09:54:23 +04:00
if ( ! vcpu - > arch . pause ) {
if ( kvm_psci_version ( source_vcpu ) ! = KVM_ARM_PSCI_0_1 )
return PSCI_RET_ALREADY_ON ;
else
return PSCI_RET_INVALID_PARAMS ;
}
2013-01-21 03:28:13 +04:00
target_pc = * vcpu_reg ( source_vcpu , 2 ) ;
2014-04-29 09:54:23 +04:00
context_id = * vcpu_reg ( source_vcpu , 3 ) ;
2013-01-21 03:28:13 +04:00
kvm_reset_vcpu ( vcpu ) ;
/* Gracefully handle Thumb2 entry point */
if ( vcpu_mode_is_32bit ( vcpu ) & & ( target_pc & 1 ) ) {
target_pc & = ~ ( ( phys_addr_t ) 1 ) ;
vcpu_set_thumb ( vcpu ) ;
}
2013-11-05 18:12:15 +04:00
/* Propagate caller endianness */
if ( kvm_vcpu_is_be ( source_vcpu ) )
kvm_vcpu_set_be ( vcpu ) ;
2013-01-21 03:28:13 +04:00
* vcpu_pc ( vcpu ) = target_pc ;
2014-04-29 09:54:23 +04:00
/*
* NOTE : We always update r0 ( or x0 ) because for PSCI v0 .1
* the general puspose registers are undefined upon CPU_ON .
*/
* vcpu_reg ( vcpu , 0 ) = context_id ;
2013-01-21 03:28:13 +04:00
vcpu - > arch . pause = false ;
smp_mb ( ) ; /* Make sure the above is visible */
2013-11-20 05:43:19 +04:00
wq = kvm_arch_vcpu_wq ( vcpu ) ;
2013-01-21 03:28:13 +04:00
wake_up_interruptible ( wq ) ;
2014-04-29 09:54:16 +04:00
return PSCI_RET_SUCCESS ;
2013-01-21 03:28:13 +04:00
}
2014-04-29 09:54:21 +04:00
static unsigned long kvm_psci_vcpu_affinity_info ( struct kvm_vcpu * vcpu )
{
int i ;
unsigned long mpidr ;
unsigned long target_affinity ;
unsigned long target_affinity_mask ;
unsigned long lowest_affinity_level ;
struct kvm * kvm = vcpu - > kvm ;
struct kvm_vcpu * tmp ;
target_affinity = * vcpu_reg ( vcpu , 1 ) ;
lowest_affinity_level = * vcpu_reg ( vcpu , 2 ) ;
/* Determine target affinity mask */
target_affinity_mask = psci_affinity_mask ( lowest_affinity_level ) ;
if ( ! target_affinity_mask )
return PSCI_RET_INVALID_PARAMS ;
/* Ignore other bits of target affinity */
target_affinity & = target_affinity_mask ;
/*
* If one or more VCPU matching target affinity are running
* then ON else OFF
*/
kvm_for_each_vcpu ( i , tmp , kvm ) {
2014-06-02 17:37:13 +04:00
mpidr = kvm_vcpu_get_mpidr_aff ( tmp ) ;
2014-04-29 09:54:21 +04:00
if ( ( ( mpidr & target_affinity_mask ) = = target_affinity ) & &
! tmp - > arch . pause ) {
return PSCI_0_2_AFFINITY_LEVEL_ON ;
}
}
return PSCI_0_2_AFFINITY_LEVEL_OFF ;
}
2014-04-29 09:54:20 +04:00
static void kvm_prepare_system_event ( struct kvm_vcpu * vcpu , u32 type )
{
2014-10-16 19:00:18 +04:00
int i ;
struct kvm_vcpu * tmp ;
/*
* The KVM ABI specifies that a system event exit may call KVM_RUN
* again and may perform shutdown / reboot at a later time that when the
* actual request is made . Since we are implementing PSCI and a
* caller of PSCI reboot and shutdown expects that the system shuts
* down or reboots immediately , let ' s make sure that VCPUs are not run
* after this call is handled and before the VCPUs have been
* re - initialized .
*/
kvm_for_each_vcpu ( i , tmp , vcpu - > kvm ) {
tmp - > arch . pause = true ;
kvm_vcpu_kick ( tmp ) ;
}
2014-04-29 09:54:20 +04:00
memset ( & vcpu - > run - > system_event , 0 , sizeof ( vcpu - > run - > system_event ) ) ;
vcpu - > run - > system_event . type = type ;
vcpu - > run - > exit_reason = KVM_EXIT_SYSTEM_EVENT ;
}
static void kvm_psci_system_off ( struct kvm_vcpu * vcpu )
{
kvm_prepare_system_event ( vcpu , KVM_SYSTEM_EVENT_SHUTDOWN ) ;
}
static void kvm_psci_system_reset ( struct kvm_vcpu * vcpu )
{
kvm_prepare_system_event ( vcpu , KVM_SYSTEM_EVENT_RESET ) ;
}
2014-04-29 09:54:16 +04:00
int kvm_psci_version ( struct kvm_vcpu * vcpu )
{
if ( test_bit ( KVM_ARM_VCPU_PSCI_0_2 , vcpu - > arch . features ) )
return KVM_ARM_PSCI_0_2 ;
return KVM_ARM_PSCI_0_1 ;
}
2014-04-29 09:54:18 +04:00
static int kvm_psci_0_2_call ( struct kvm_vcpu * vcpu )
2014-04-29 09:54:16 +04:00
{
2014-04-29 09:54:20 +04:00
int ret = 1 ;
2014-04-29 09:54:16 +04:00
unsigned long psci_fn = * vcpu_reg ( vcpu , 0 ) & ~ ( ( u32 ) 0 ) ;
unsigned long val ;
switch ( psci_fn ) {
case PSCI_0_2_FN_PSCI_VERSION :
/*
* Bits [ 31 : 16 ] = Major Version = 0
* Bits [ 15 : 0 ] = Minor Version = 2
*/
val = 2 ;
break ;
2014-04-29 09:54:24 +04:00
case PSCI_0_2_FN_CPU_SUSPEND :
case PSCI_0_2_FN64_CPU_SUSPEND :
val = kvm_psci_vcpu_suspend ( vcpu ) ;
break ;
2014-04-29 09:54:16 +04:00
case PSCI_0_2_FN_CPU_OFF :
kvm_psci_vcpu_off ( vcpu ) ;
val = PSCI_RET_SUCCESS ;
break ;
case PSCI_0_2_FN_CPU_ON :
case PSCI_0_2_FN64_CPU_ON :
val = kvm_psci_vcpu_on ( vcpu ) ;
break ;
2014-04-29 09:54:21 +04:00
case PSCI_0_2_FN_AFFINITY_INFO :
case PSCI_0_2_FN64_AFFINITY_INFO :
val = kvm_psci_vcpu_affinity_info ( vcpu ) ;
break ;
2014-04-29 09:54:22 +04:00
case PSCI_0_2_FN_MIGRATE :
case PSCI_0_2_FN64_MIGRATE :
val = PSCI_RET_NOT_SUPPORTED ;
break ;
case PSCI_0_2_FN_MIGRATE_INFO_TYPE :
/*
* Trusted OS is MP hence does not require migration
* or
* Trusted OS is not present
*/
val = PSCI_0_2_TOS_MP ;
break ;
case PSCI_0_2_FN_MIGRATE_INFO_UP_CPU :
case PSCI_0_2_FN64_MIGRATE_INFO_UP_CPU :
val = PSCI_RET_NOT_SUPPORTED ;
break ;
2014-04-29 09:54:20 +04:00
case PSCI_0_2_FN_SYSTEM_OFF :
kvm_psci_system_off ( vcpu ) ;
/*
* We should ' nt be going back to guest VCPU after
* receiving SYSTEM_OFF request .
*
* If user space accidently / deliberately resumes
* guest VCPU after SYSTEM_OFF request then guest
* VCPU should see internal failure from PSCI return
* value . To achieve this , we preload r0 ( or x0 ) with
* PSCI return value INTERNAL_FAILURE .
*/
val = PSCI_RET_INTERNAL_FAILURE ;
ret = 0 ;
break ;
case PSCI_0_2_FN_SYSTEM_RESET :
kvm_psci_system_reset ( vcpu ) ;
/*
* Same reason as SYSTEM_OFF for preloading r0 ( or x0 )
* with PSCI return value INTERNAL_FAILURE .
*/
val = PSCI_RET_INTERNAL_FAILURE ;
ret = 0 ;
break ;
2014-04-29 09:54:16 +04:00
default :
2014-04-29 09:54:18 +04:00
return - EINVAL ;
2014-04-29 09:54:16 +04:00
}
* vcpu_reg ( vcpu , 0 ) = val ;
2014-04-29 09:54:20 +04:00
return ret ;
2014-04-29 09:54:16 +04:00
}
2014-04-29 09:54:18 +04:00
static int kvm_psci_0_1_call ( struct kvm_vcpu * vcpu )
2013-01-21 03:28:13 +04:00
{
unsigned long psci_fn = * vcpu_reg ( vcpu , 0 ) & ~ ( ( u32 ) 0 ) ;
unsigned long val ;
switch ( psci_fn ) {
case KVM_PSCI_FN_CPU_OFF :
kvm_psci_vcpu_off ( vcpu ) ;
2014-04-29 09:54:16 +04:00
val = PSCI_RET_SUCCESS ;
2013-01-21 03:28:13 +04:00
break ;
case KVM_PSCI_FN_CPU_ON :
val = kvm_psci_vcpu_on ( vcpu ) ;
break ;
case KVM_PSCI_FN_CPU_SUSPEND :
case KVM_PSCI_FN_MIGRATE :
2014-04-29 09:54:16 +04:00
val = PSCI_RET_NOT_SUPPORTED ;
2013-01-21 03:28:13 +04:00
break ;
default :
2014-04-29 09:54:18 +04:00
return - EINVAL ;
2013-01-21 03:28:13 +04:00
}
* vcpu_reg ( vcpu , 0 ) = val ;
2014-04-29 09:54:18 +04:00
return 1 ;
2013-01-21 03:28:13 +04:00
}
2014-04-29 09:54:16 +04:00
/**
* kvm_psci_call - handle PSCI call if r0 value is in range
* @ vcpu : Pointer to the VCPU struct
*
* Handle PSCI calls from guests through traps from HVC instructions .
2014-04-29 09:54:18 +04:00
* The calling convention is similar to SMC calls to the secure world
* where the function number is placed in r0 .
*
* This function returns : > 0 ( success ) , 0 ( success but exit to user
* space ) , and < 0 ( errors )
*
* Errors :
* - EINVAL : Unrecognized PSCI function
2014-04-29 09:54:16 +04:00
*/
2014-04-29 09:54:18 +04:00
int kvm_psci_call ( struct kvm_vcpu * vcpu )
2014-04-29 09:54:16 +04:00
{
switch ( kvm_psci_version ( vcpu ) ) {
case KVM_ARM_PSCI_0_2 :
return kvm_psci_0_2_call ( vcpu ) ;
case KVM_ARM_PSCI_0_1 :
return kvm_psci_0_1_call ( vcpu ) ;
default :
2014-04-29 09:54:18 +04:00
return - EINVAL ;
2014-04-29 09:54:16 +04:00
} ;
}