2019-05-27 09:55:21 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2017-01-26 17:20:51 +03:00
/*
* VGIC system registers handling functions for AArch64 mode
*/
# include <linux/irqchip/arm-gic-v3.h>
# include <linux/kvm.h>
# include <linux/kvm_host.h>
# include <asm/kvm_emulate.h>
2020-05-13 13:40:34 +03:00
# include "vgic/vgic.h"
2017-01-26 17:20:51 +03:00
# include "sys_regs.h"
static bool access_gic_ctlr ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r )
{
u32 host_pri_bits , host_id_bits , host_seis , host_a3v , seis , a3v ;
struct vgic_cpu * vgic_v3_cpu = & vcpu - > arch . vgic_cpu ;
struct vgic_vmcr vmcr ;
u64 val ;
vgic_get_vmcr ( vcpu , & vmcr ) ;
if ( p - > is_write ) {
val = p - > regval ;
/*
* Disallow restoring VM state if not supported by this
* hardware .
*/
host_pri_bits = ( ( val & ICC_CTLR_EL1_PRI_BITS_MASK ) > >
ICC_CTLR_EL1_PRI_BITS_SHIFT ) + 1 ;
if ( host_pri_bits > vgic_v3_cpu - > num_pri_bits )
return false ;
vgic_v3_cpu - > num_pri_bits = host_pri_bits ;
host_id_bits = ( val & ICC_CTLR_EL1_ID_BITS_MASK ) > >
ICC_CTLR_EL1_ID_BITS_SHIFT ;
if ( host_id_bits > vgic_v3_cpu - > num_id_bits )
return false ;
vgic_v3_cpu - > num_id_bits = host_id_bits ;
host_seis = ( ( kvm_vgic_global_state . ich_vtr_el2 &
ICH_VTR_SEIS_MASK ) > > ICH_VTR_SEIS_SHIFT ) ;
seis = ( val & ICC_CTLR_EL1_SEIS_MASK ) > >
ICC_CTLR_EL1_SEIS_SHIFT ;
if ( host_seis ! = seis )
return false ;
host_a3v = ( ( kvm_vgic_global_state . ich_vtr_el2 &
ICH_VTR_A3V_MASK ) > > ICH_VTR_A3V_SHIFT ) ;
a3v = ( val & ICC_CTLR_EL1_A3V_MASK ) > > ICC_CTLR_EL1_A3V_SHIFT ;
if ( host_a3v ! = a3v )
return false ;
/*
* Here set VMCR . CTLR in ICC_CTLR_EL1 layout .
* The vgic_set_vmcr ( ) will convert to ICH_VMCR layout .
*/
2017-05-20 15:12:34 +03:00
vmcr . cbpr = ( val & ICC_CTLR_EL1_CBPR_MASK ) > > ICC_CTLR_EL1_CBPR_SHIFT ;
vmcr . eoim = ( val & ICC_CTLR_EL1_EOImode_MASK ) > > ICC_CTLR_EL1_EOImode_SHIFT ;
2017-01-26 17:20:51 +03:00
vgic_set_vmcr ( vcpu , & vmcr ) ;
} else {
val = 0 ;
val | = ( vgic_v3_cpu - > num_pri_bits - 1 ) < <
ICC_CTLR_EL1_PRI_BITS_SHIFT ;
val | = vgic_v3_cpu - > num_id_bits < < ICC_CTLR_EL1_ID_BITS_SHIFT ;
val | = ( ( kvm_vgic_global_state . ich_vtr_el2 &
ICH_VTR_SEIS_MASK ) > > ICH_VTR_SEIS_SHIFT ) < <
ICC_CTLR_EL1_SEIS_SHIFT ;
val | = ( ( kvm_vgic_global_state . ich_vtr_el2 &
ICH_VTR_A3V_MASK ) > > ICH_VTR_A3V_SHIFT ) < <
ICC_CTLR_EL1_A3V_SHIFT ;
/*
* The VMCR . CTLR value is in ICC_CTLR_EL1 layout .
* Extract it directly using ICC_CTLR_EL1 reg definitions .
*/
2017-05-20 15:12:34 +03:00
val | = ( vmcr . cbpr < < ICC_CTLR_EL1_CBPR_SHIFT ) & ICC_CTLR_EL1_CBPR_MASK ;
val | = ( vmcr . eoim < < ICC_CTLR_EL1_EOImode_SHIFT ) & ICC_CTLR_EL1_EOImode_MASK ;
2017-01-26 17:20:51 +03:00
p - > regval = val ;
}
return true ;
}
static bool access_gic_pmr ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r )
{
struct vgic_vmcr vmcr ;
vgic_get_vmcr ( vcpu , & vmcr ) ;
if ( p - > is_write ) {
vmcr . pmr = ( p - > regval & ICC_PMR_EL1_MASK ) > > ICC_PMR_EL1_SHIFT ;
vgic_set_vmcr ( vcpu , & vmcr ) ;
} else {
p - > regval = ( vmcr . pmr < < ICC_PMR_EL1_SHIFT ) & ICC_PMR_EL1_MASK ;
}
return true ;
}
static bool access_gic_bpr0 ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r )
{
struct vgic_vmcr vmcr ;
vgic_get_vmcr ( vcpu , & vmcr ) ;
if ( p - > is_write ) {
vmcr . bpr = ( p - > regval & ICC_BPR0_EL1_MASK ) > >
ICC_BPR0_EL1_SHIFT ;
vgic_set_vmcr ( vcpu , & vmcr ) ;
} else {
p - > regval = ( vmcr . bpr < < ICC_BPR0_EL1_SHIFT ) &
ICC_BPR0_EL1_MASK ;
}
return true ;
}
static bool access_gic_bpr1 ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r )
{
struct vgic_vmcr vmcr ;
if ( ! p - > is_write )
p - > regval = 0 ;
vgic_get_vmcr ( vcpu , & vmcr ) ;
2017-05-20 15:12:34 +03:00
if ( ! vmcr . cbpr ) {
2017-01-26 17:20:51 +03:00
if ( p - > is_write ) {
vmcr . abpr = ( p - > regval & ICC_BPR1_EL1_MASK ) > >
ICC_BPR1_EL1_SHIFT ;
vgic_set_vmcr ( vcpu , & vmcr ) ;
} else {
p - > regval = ( vmcr . abpr < < ICC_BPR1_EL1_SHIFT ) &
ICC_BPR1_EL1_MASK ;
}
} else {
if ( ! p - > is_write )
p - > regval = min ( ( vmcr . bpr + 1 ) , 7U ) ;
}
return true ;
}
static bool access_gic_grpen0 ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r )
{
struct vgic_vmcr vmcr ;
vgic_get_vmcr ( vcpu , & vmcr ) ;
if ( p - > is_write ) {
vmcr . grpen0 = ( p - > regval & ICC_IGRPEN0_EL1_MASK ) > >
ICC_IGRPEN0_EL1_SHIFT ;
vgic_set_vmcr ( vcpu , & vmcr ) ;
} else {
p - > regval = ( vmcr . grpen0 < < ICC_IGRPEN0_EL1_SHIFT ) &
ICC_IGRPEN0_EL1_MASK ;
}
return true ;
}
static bool access_gic_grpen1 ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r )
{
struct vgic_vmcr vmcr ;
vgic_get_vmcr ( vcpu , & vmcr ) ;
if ( p - > is_write ) {
vmcr . grpen1 = ( p - > regval & ICC_IGRPEN1_EL1_MASK ) > >
ICC_IGRPEN1_EL1_SHIFT ;
vgic_set_vmcr ( vcpu , & vmcr ) ;
} else {
p - > regval = ( vmcr . grpen1 < < ICC_IGRPEN1_EL1_SHIFT ) &
ICC_IGRPEN1_EL1_MASK ;
}
return true ;
}
static void vgic_v3_access_apr_reg ( struct kvm_vcpu * vcpu ,
struct sys_reg_params * p , u8 apr , u8 idx )
{
struct vgic_v3_cpu_if * vgicv3 = & vcpu - > arch . vgic_cpu . vgic_v3 ;
uint32_t * ap_reg ;
if ( apr )
ap_reg = & vgicv3 - > vgic_ap1r [ idx ] ;
else
ap_reg = & vgicv3 - > vgic_ap0r [ idx ] ;
if ( p - > is_write )
* ap_reg = p - > regval ;
else
p - > regval = * ap_reg ;
}
static bool access_gic_aprn ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r , u8 apr )
{
u8 idx = r - > Op2 & 3 ;
2017-09-01 12:41:52 +03:00
if ( idx > vgic_v3_max_apr_idx ( vcpu ) )
goto err ;
2017-01-26 17:20:51 +03:00
2017-09-01 12:41:52 +03:00
vgic_v3_access_apr_reg ( vcpu , p , apr , idx ) ;
2017-01-26 17:20:51 +03:00
return true ;
err :
if ( ! p - > is_write )
p - > regval = 0 ;
return false ;
}
static bool access_gic_ap0r ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r )
{
return access_gic_aprn ( vcpu , p , r , 0 ) ;
}
static bool access_gic_ap1r ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r )
{
return access_gic_aprn ( vcpu , p , r , 1 ) ;
}
static bool access_gic_sre ( struct kvm_vcpu * vcpu , struct sys_reg_params * p ,
const struct sys_reg_desc * r )
{
struct vgic_v3_cpu_if * vgicv3 = & vcpu - > arch . vgic_cpu . vgic_v3 ;
/* Validate SRE bit */
if ( p - > is_write ) {
if ( ! ( p - > regval & ICC_SRE_EL1_SRE ) )
return false ;
} else {
p - > regval = vgicv3 - > vgic_sre ;
}
return true ;
}
static const struct sys_reg_desc gic_v3_icc_reg_descs [ ] = {
2017-06-05 16:20:01 +03:00
{ SYS_DESC ( SYS_ICC_PMR_EL1 ) , access_gic_pmr } ,
{ SYS_DESC ( SYS_ICC_BPR0_EL1 ) , access_gic_bpr0 } ,
{ SYS_DESC ( SYS_ICC_AP0R0_EL1 ) , access_gic_ap0r } ,
{ SYS_DESC ( SYS_ICC_AP0R1_EL1 ) , access_gic_ap0r } ,
{ SYS_DESC ( SYS_ICC_AP0R2_EL1 ) , access_gic_ap0r } ,
{ SYS_DESC ( SYS_ICC_AP0R3_EL1 ) , access_gic_ap0r } ,
{ SYS_DESC ( SYS_ICC_AP1R0_EL1 ) , access_gic_ap1r } ,
{ SYS_DESC ( SYS_ICC_AP1R1_EL1 ) , access_gic_ap1r } ,
{ SYS_DESC ( SYS_ICC_AP1R2_EL1 ) , access_gic_ap1r } ,
{ SYS_DESC ( SYS_ICC_AP1R3_EL1 ) , access_gic_ap1r } ,
{ SYS_DESC ( SYS_ICC_BPR1_EL1 ) , access_gic_bpr1 } ,
{ SYS_DESC ( SYS_ICC_CTLR_EL1 ) , access_gic_ctlr } ,
{ SYS_DESC ( SYS_ICC_SRE_EL1 ) , access_gic_sre } ,
{ SYS_DESC ( SYS_ICC_IGRPEN0_EL1 ) , access_gic_grpen0 } ,
{ SYS_DESC ( SYS_ICC_IGRPEN1_EL1 ) , access_gic_grpen1 } ,
2017-01-26 17:20:51 +03:00
} ;
int vgic_v3_has_cpu_sysregs_attr ( struct kvm_vcpu * vcpu , bool is_write , u64 id ,
u64 * reg )
{
struct sys_reg_params params ;
u64 sysreg = ( id & KVM_DEV_ARM_VGIC_SYSREG_MASK ) | KVM_REG_SIZE_U64 ;
params . regval = * reg ;
params . is_write = is_write ;
if ( find_reg_by_id ( sysreg , & params , gic_v3_icc_reg_descs ,
ARRAY_SIZE ( gic_v3_icc_reg_descs ) ) )
return 0 ;
return - ENXIO ;
}
int vgic_v3_cpu_sysregs_uaccess ( struct kvm_vcpu * vcpu , bool is_write , u64 id ,
u64 * reg )
{
struct sys_reg_params params ;
const struct sys_reg_desc * r ;
u64 sysreg = ( id & KVM_DEV_ARM_VGIC_SYSREG_MASK ) | KVM_REG_SIZE_U64 ;
if ( is_write )
params . regval = * reg ;
params . is_write = is_write ;
r = find_reg_by_id ( sysreg , & params , gic_v3_icc_reg_descs ,
ARRAY_SIZE ( gic_v3_icc_reg_descs ) ) ;
if ( ! r )
return - ENXIO ;
if ( ! r - > access ( vcpu , & params , r ) )
return - EINVAL ;
if ( ! is_write )
* reg = params . regval ;
return 0 ;
}