2015-12-01 14:34:34 +00:00
/*
* VGICv3 MMIO handling functions
*
* 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 .
*/
# include <linux/irqchip/arm-gic-v3.h>
# include <linux/kvm.h>
# include <linux/kvm_host.h>
# include <kvm/iodev.h>
# include <kvm/arm_vgic.h>
# include <asm/kvm_emulate.h>
# include "vgic.h"
# include "vgic-mmio.h"
2016-01-27 14:54:46 +00:00
/* extract @num bytes at @offset bytes offset in data */
static unsigned long extract_bytes ( unsigned long data , unsigned int offset ,
unsigned int num )
{
return ( data > > ( offset * 8 ) ) & GENMASK_ULL ( num * 8 - 1 , 0 ) ;
}
2016-01-27 14:54:30 +00:00
static unsigned long vgic_mmio_read_v3_misc ( struct kvm_vcpu * vcpu ,
gpa_t addr , unsigned int len )
{
u32 value = 0 ;
switch ( addr & 0x0c ) {
case GICD_CTLR :
if ( vcpu - > kvm - > arch . vgic . enabled )
value | = GICD_CTLR_ENABLE_SS_G1 ;
value | = GICD_CTLR_ARE_NS | GICD_CTLR_DS ;
break ;
case GICD_TYPER :
value = vcpu - > kvm - > arch . vgic . nr_spis + VGIC_NR_PRIVATE_IRQS ;
value = ( value > > 5 ) - 1 ;
value | = ( INTERRUPT_ID_BITS_SPIS - 1 ) < < 19 ;
break ;
case GICD_IIDR :
value = ( PRODUCT_ID_KVM < < 24 ) | ( IMPLEMENTER_ARM < < 0 ) ;
break ;
default :
return 0 ;
}
return value ;
}
static void vgic_mmio_write_v3_misc ( struct kvm_vcpu * vcpu ,
gpa_t addr , unsigned int len ,
unsigned long val )
{
struct vgic_dist * dist = & vcpu - > kvm - > arch . vgic ;
bool was_enabled = dist - > enabled ;
switch ( addr & 0x0c ) {
case GICD_CTLR :
dist - > enabled = val & GICD_CTLR_ENABLE_SS_G1 ;
if ( ! was_enabled & & dist - > enabled )
vgic_kick_vcpus ( vcpu - > kvm ) ;
break ;
case GICD_TYPER :
case GICD_IIDR :
return ;
}
}
2016-01-27 14:54:46 +00:00
static unsigned long vgic_mmio_read_v3r_typer ( struct kvm_vcpu * vcpu ,
gpa_t addr , unsigned int len )
{
unsigned long mpidr = kvm_vcpu_get_mpidr_aff ( vcpu ) ;
int target_vcpu_id = vcpu - > vcpu_id ;
u64 value ;
value = ( mpidr & GENMASK ( 23 , 0 ) ) < < 32 ;
value | = ( ( target_vcpu_id & 0xffff ) < < 8 ) ;
if ( target_vcpu_id = = atomic_read ( & vcpu - > kvm - > online_vcpus ) - 1 )
value | = GICR_TYPER_LAST ;
return extract_bytes ( value , addr & 7 , len ) ;
}
static unsigned long vgic_mmio_read_v3r_iidr ( struct kvm_vcpu * vcpu ,
gpa_t addr , unsigned int len )
{
return ( PRODUCT_ID_KVM < < 24 ) | ( IMPLEMENTER_ARM < < 0 ) ;
}
2015-12-01 14:34:34 +00:00
/*
* The GICv3 per - IRQ registers are split to control PPIs and SGIs in the
* redistributors , while SPIs are covered by registers in the distributor
* block . Trying to set private IRQs in this block gets ignored .
* We take some special care here to fix the calculation of the register
* offset .
*/
# define REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED(off, rd, wr, bpi, acc) \
{ \
. reg_offset = off , \
. bits_per_irq = bpi , \
. len = ( bpi * VGIC_NR_PRIVATE_IRQS ) / 8 , \
. access_flags = acc , \
. read = vgic_mmio_read_raz , \
. write = vgic_mmio_write_wi , \
} , { \
. reg_offset = off + ( bpi * VGIC_NR_PRIVATE_IRQS ) / 8 , \
. bits_per_irq = bpi , \
. len = ( bpi * ( 1024 - VGIC_NR_PRIVATE_IRQS ) ) / 8 , \
. access_flags = acc , \
. read = rd , \
. write = wr , \
}
static const struct vgic_register_region vgic_v3_dist_registers [ ] = {
REGISTER_DESC_WITH_LENGTH ( GICD_CTLR ,
2016-01-27 14:54:30 +00:00
vgic_mmio_read_v3_misc , vgic_mmio_write_v3_misc , 16 ,
2015-12-01 14:34:34 +00:00
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_IGROUPR ,
vgic_mmio_read_rao , vgic_mmio_write_wi , 1 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_ISENABLER ,
vgic_mmio_read_enable , vgic_mmio_write_senable , 1 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_ICENABLER ,
vgic_mmio_read_enable , vgic_mmio_write_cenable , 1 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_ISPENDR ,
vgic_mmio_read_pending , vgic_mmio_write_spending , 1 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_ICPENDR ,
vgic_mmio_read_pending , vgic_mmio_write_cpending , 1 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_ISACTIVER ,
vgic_mmio_read_active , vgic_mmio_write_sactive , 1 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_ICACTIVER ,
vgic_mmio_read_active , vgic_mmio_write_cactive , 1 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_IPRIORITYR ,
vgic_mmio_read_priority , vgic_mmio_write_priority , 8 ,
VGIC_ACCESS_32bit | VGIC_ACCESS_8bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_ITARGETSR ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 8 ,
VGIC_ACCESS_32bit | VGIC_ACCESS_8bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_ICFGR ,
vgic_mmio_read_config , vgic_mmio_write_config , 2 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_IGRPMODR ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 1 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_BITS_PER_IRQ_SHARED ( GICD_IROUTER ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 64 ,
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICD_IDREGS ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 48 ,
VGIC_ACCESS_32bit ) ,
} ;
static const struct vgic_register_region vgic_v3_rdbase_registers [ ] = {
REGISTER_DESC_WITH_LENGTH ( GICR_CTLR ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 4 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_IIDR ,
2016-01-27 14:54:46 +00:00
vgic_mmio_read_v3r_iidr , vgic_mmio_write_wi , 4 ,
2015-12-01 14:34:34 +00:00
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_TYPER ,
2016-01-27 14:54:46 +00:00
vgic_mmio_read_v3r_typer , vgic_mmio_write_wi , 8 ,
2015-12-01 14:34:34 +00:00
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_PROPBASER ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 8 ,
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_PENDBASER ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 8 ,
VGIC_ACCESS_64bit | VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_IDREGS ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 48 ,
VGIC_ACCESS_32bit ) ,
} ;
static const struct vgic_register_region vgic_v3_sgibase_registers [ ] = {
REGISTER_DESC_WITH_LENGTH ( GICR_IGROUPR0 ,
vgic_mmio_read_rao , vgic_mmio_write_wi , 4 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_ISENABLER0 ,
vgic_mmio_read_enable , vgic_mmio_write_senable , 4 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_ICENABLER0 ,
vgic_mmio_read_enable , vgic_mmio_write_cenable , 4 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_ISPENDR0 ,
vgic_mmio_read_pending , vgic_mmio_write_spending , 4 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_ICPENDR0 ,
vgic_mmio_read_pending , vgic_mmio_write_cpending , 4 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_ISACTIVER0 ,
vgic_mmio_read_active , vgic_mmio_write_sactive , 4 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_ICACTIVER0 ,
vgic_mmio_read_active , vgic_mmio_write_cactive , 4 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_IPRIORITYR0 ,
vgic_mmio_read_priority , vgic_mmio_write_priority , 32 ,
VGIC_ACCESS_32bit | VGIC_ACCESS_8bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_ICFGR0 ,
vgic_mmio_read_config , vgic_mmio_write_config , 8 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_IGRPMODR0 ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 4 ,
VGIC_ACCESS_32bit ) ,
REGISTER_DESC_WITH_LENGTH ( GICR_NSACR ,
vgic_mmio_read_raz , vgic_mmio_write_wi , 4 ,
VGIC_ACCESS_32bit ) ,
} ;
unsigned int vgic_v3_init_dist_iodev ( struct vgic_io_device * dev )
{
dev - > regions = vgic_v3_dist_registers ;
dev - > nr_regions = ARRAY_SIZE ( vgic_v3_dist_registers ) ;
kvm_iodevice_init ( & dev - > dev , & kvm_io_gic_ops ) ;
return SZ_64K ;
}
int vgic_register_redist_iodevs ( struct kvm * kvm , gpa_t redist_base_address )
{
int nr_vcpus = atomic_read ( & kvm - > online_vcpus ) ;
struct kvm_vcpu * vcpu ;
struct vgic_io_device * devices ;
int c , ret = 0 ;
devices = kmalloc ( sizeof ( struct vgic_io_device ) * nr_vcpus * 2 ,
GFP_KERNEL ) ;
if ( ! devices )
return - ENOMEM ;
kvm_for_each_vcpu ( c , vcpu , kvm ) {
gpa_t rd_base = redist_base_address + c * SZ_64K * 2 ;
gpa_t sgi_base = rd_base + SZ_64K ;
struct vgic_io_device * rd_dev = & devices [ c * 2 ] ;
struct vgic_io_device * sgi_dev = & devices [ c * 2 + 1 ] ;
kvm_iodevice_init ( & rd_dev - > dev , & kvm_io_gic_ops ) ;
rd_dev - > base_addr = rd_base ;
rd_dev - > regions = vgic_v3_rdbase_registers ;
rd_dev - > nr_regions = ARRAY_SIZE ( vgic_v3_rdbase_registers ) ;
rd_dev - > redist_vcpu = vcpu ;
mutex_lock ( & kvm - > slots_lock ) ;
ret = kvm_io_bus_register_dev ( kvm , KVM_MMIO_BUS , rd_base ,
SZ_64K , & rd_dev - > dev ) ;
mutex_unlock ( & kvm - > slots_lock ) ;
if ( ret )
break ;
kvm_iodevice_init ( & sgi_dev - > dev , & kvm_io_gic_ops ) ;
sgi_dev - > base_addr = sgi_base ;
sgi_dev - > regions = vgic_v3_sgibase_registers ;
sgi_dev - > nr_regions = ARRAY_SIZE ( vgic_v3_sgibase_registers ) ;
sgi_dev - > redist_vcpu = vcpu ;
mutex_lock ( & kvm - > slots_lock ) ;
ret = kvm_io_bus_register_dev ( kvm , KVM_MMIO_BUS , sgi_base ,
SZ_64K , & sgi_dev - > dev ) ;
mutex_unlock ( & kvm - > slots_lock ) ;
if ( ret ) {
kvm_io_bus_unregister_dev ( kvm , KVM_MMIO_BUS ,
& rd_dev - > dev ) ;
break ;
}
}
if ( ret ) {
/* The current c failed, so we start with the previous one. */
for ( c - - ; c > = 0 ; c - - ) {
kvm_io_bus_unregister_dev ( kvm , KVM_MMIO_BUS ,
& devices [ c * 2 ] . dev ) ;
kvm_io_bus_unregister_dev ( kvm , KVM_MMIO_BUS ,
& devices [ c * 2 + 1 ] . dev ) ;
}
kfree ( devices ) ;
} else {
kvm - > arch . vgic . redist_iodevs = devices ;
}
return ret ;
}